<template>
  <div class="ht-chart">
    <el-form
      v-if="chartCountType == 1"
      v-model="countQuery"
      :class="[fullScreen && isMobile ? 'horizontal-echarts-filters' : '']"
    >
      <div v-if="isMobile" class="line">
        <div class="dropDown">
          <van-dropdown-menu
            v-model="dropdownActive"
            active-color="#409EFF"
            :class="['dropdown-menu', filterType == 'policy' ? 'active' : '']"
          >
            <van-dropdown-item
              v-model="countQuery.countField"
              title="统计字段"
              :options="mobileCountFields"
              :title-class="countQuery.countField != '' ? 'countField' : ''"
            />
            <van-dropdown-item
              ref="dropdown-item-more"
              class="query-params-more-dropdown"
              :title-class="orderTypeSelect ? 'orderSelect' : 'moreSelect'"
            >
              <template #title>
                <div class="more_btn">
                  <div class="text" @click="changeCountField">统计类型</div>
                  <ht-icon
                    name="tongjileixing"
                    width="16"
                    height="16"
                    style="margin-left: 6px"
                  />
                </div>
              </template>
              <van-popup
                v-model="showType"
                position="bottom"
                :style="{ height: '80%' }"
                class="mobile-van-action-chart more-dropdown-popup"
                round
                get-container="body"
                :class="{
                  'fullscreen-popup': fullScreen && isMobile,
                  'fullscreen-popup-active': fullScreen && isMobile,
                }"
                @closed="closeAllPopup"
              >
                <div class="pop-header">
                  <span class="cancel" @click="showType = false">关闭</span>
                  <span class="title">筛选</span>
                  <span></span>
                </div>
                <div
                  :style="{ height: 'calc(100% - 110px)' }"
                  style="overflow-y: auto"
                >
                  <van-field
                    readonly
                    clickable
                    :value="getCountType()"
                    label="统计类型"
                    placeholder="选择统计类型"
                    :class="{ 'fullscreen-input': fullScreen && isMobile }"
                    @click="showCountType = true"
                  />
                  <van-popup
                    v-model="showCountType"
                    position="bottom"
                    :class="{
                      'fullscreen-popup': fullScreen && isMobile,
                      'fullscreen-popup-active': fullScreen && isMobile,
                    }"
                  >
                    <van-picker
                      v-model="countQuery.countType"
                      show-toolbar
                      title="统计类型"
                      :columns="countTypes"
                      value-key="label"
                      @confirm="onConfirmCountType"
                      @cancel="showCountType = false"
                    ></van-picker>
                  </van-popup>
                  <van-field
                    v-if="countQuery.countType !== 'count'"
                    readonly
                    clickable
                    :value="getCalcField()"
                    label="计算字段"
                    placeholder="选择计算字段"
                    :class="{ 'fullscreen-input': fullScreen && isMobile }"
                    @click="showCalcField = true"
                  />
                  <van-popup
                    v-model="showCalcField"
                    position="bottom"
                    :class="{
                      'fullscreen-popup': fullScreen && isMobile,
                      'fullscreen-popup-active': fullScreen && isMobile,
                    }"
                  >
                    <van-picker
                      v-model="countQuery.calcField"
                      show-toolbar
                      title="计算字段"
                      :columns="countFields"
                      value-key="comment"
                      @confirm="onConfirmCalcField"
                      @cancel="showCalcField = false"
                    ></van-picker>
                  </van-popup>
                </div>
                <div
                  class="search-btns"
                  style="
                    position: fixed;
                    bottom: 0;
                    width: 100%;
                    box-sizing: border-box;
                  "
                >
                  <van-button type="default" @click="resetCountOrder">
                    重置
                  </van-button>
                  <van-button
                    style="margin-left: 10px"
                    type="info"
                    @click="hideCountSearch()"
                  >
                    完成
                  </van-button>
                </div>
              </van-popup>
            </van-dropdown-item>
            <van-dropdown-item
              ref="dropdown-item-more"
              class="query-params-more-dropdown"
              :title-class="orderSelect ? 'orderSelect' : 'moreSelect'"
            >
              <template #title>
                <div class="more_btn">
                  <div class="text" @click="changeMore">筛选</div>
                  <ht-icon
                    name="shaixuan"
                    width="16"
                    height="16"
                    style="margin-left: 4px"
                  />
                </div>
              </template>
              <van-popup
                v-model="show"
                position="bottom"
                :style="{ height: '80%' }"
                class="mobile-van-action-chart more-dropdown-popup"
                round
                get-container="body"
                :class="{
                  'fullscreen-popup': fullScreen && isMobile,
                  'fullscreen-popup-active': fullScreen && isMobile,
                }"
                @closed="closeAllPopup"
              >
                <div class="pop-header">
                  <span class="cancel" @click="show = false">关闭</span>
                  <span class="title">筛选</span>
                  <span></span>
                </div>
                <div
                  :style="{ height: 'calc(100% - 110px)' }"
                  style="overflow-y: auto"
                >
                  <van-cell-group
                    v-for="(item, index) in countQueryFileds"
                    :key="index"
                  >
                    <van-field
                      v-model="countQuery[countQueryFileds[index].field]"
                      :placeholder="`请输入${countQueryFileds[index].comment}`"
                      :label="countQueryFileds[index].comment"
                      :class="{ 'fullscreen-input': fullScreen && isMobile }"
                    ></van-field>
                  </van-cell-group>
                </div>
                <div
                  class="search-btns"
                  style="
                    position: fixed;
                    bottom: 0;
                    width: 100%;
                    box-sizing: border-box;
                  "
                >
                  <van-button type="default" @click="resetOrder">
                    重置
                  </van-button>
                  <van-button
                    style="margin-left: 10px"
                    type="info"
                    @click="hideSearch()"
                  >
                    完成
                  </van-button>
                </div>
              </van-popup>
            </van-dropdown-item>
          </van-dropdown-menu>
        </div>
      </div>
      <div v-else>
        <div
          style="
            max-height: 150px;
            overflow-y: auto;
            padding-right: 10px;
            margin-right: -10px;
            margin-top: 10px;
            margin-bottom: 10px;
          "
        >
          <el-row v-for="(item, index) in countQueryFileds" :key="index">
            <el-col v-if="countQueryFileds[index] && index % 2 == 0" :span="12">
              <ht-form-item :label="countQueryFileds[index].comment">
                <el-input
                  v-model="countQuery[countQueryFileds[index].field]"
                  clearable
                ></el-input>
              </ht-form-item>
            </el-col>
            <el-col
              v-if="countQueryFileds[index + 1] && index % 2 == 0"
              :span="12"
            >
              <ht-form-item :label="countQueryFileds[index + 1].comment">
                <el-input
                  v-model="countQuery[countQueryFileds[index + 1].field]"
                  clearable
                ></el-input>
              </ht-form-item>
            </el-col>
          </el-row>
        </div>
        <el-row type="flex">
          <el-col :span="7">
            <ht-form-item label="统计字段">
              <el-select
                v-model="countQuery.countField"
                placeholder="请选择"
                style="width: 100%"
                @change="searchData"
              >
                <el-option
                  v-for="item in countFields"
                  :key="item.field"
                  :label="item.comment"
                  :value="item.field"
                ></el-option>
              </el-select>
            </ht-form-item>
          </el-col>
          <el-col :span="7">
            <ht-form-item label="统计类型">
              <el-select
                v-model="countQuery.countType"
                placeholder="请选择"
                style="width: 100%"
                @change="searchData"
              >
                <el-option
                  v-for="item in countTypes"
                  :key="item.value"
                  :label="item.label"
                  :value="item.value"
                ></el-option>
              </el-select>
            </ht-form-item>
          </el-col>
          <el-col v-if="countQuery.countType !== 'count'" :span="7">
            <ht-form-item label="计算字段">
              <el-select
                v-model="countQuery.calcField"
                placeholder="请选择"
                style="width: 100%"
                @change="searchData"
              >
                <el-option
                  v-for="item in countFields"
                  :key="item.field"
                  :label="item.comment"
                  :value="item.field"
                ></el-option>
              </el-select>
            </ht-form-item>
          </el-col>
          <el-col :span="3">
            <el-button type="primary" @click="searchData">查询</el-button>
          </el-col>
        </el-row>
      </div>
    </el-form>
    <div v-if="permission != 'n' && !isMobile" :style="chartStyle">
      <div
        v-if="
          (customChart.conf && customChart.conf.series.showChartType != 3) ||
          isProcessChart
        "
        style="width: 100%"
      >
        <el-switch
          v-if="showColorModeType"
          v-model="isDark"
          active-text="深色模式"
          inactive-text="浅色模式"
          style="margin-bottom: 5px"
          @change="modeChange"
        ></el-switch>
        <div
          v-if="showTable"
          :id="chartKey"
          :key="chartKey"
          :ref="`${chartKey}myChart`"
          style="min-height: 300px"
          :style="{ width: charWidth, height: charHeight }"
        ></div>
      </div>
      <div
        v-if="customChart.conf && customChart.conf.series.showChartType != 1"
        style="margin-top: 25px; width: 100%"
      >
        <div v-if="isProcessChart" v-html="optionToContent(option)"></div>
        <div v-if="showTable && !isProcessChart">
          <div
            v-if="[3, 4].includes(customChart.conf.series.showChartLayout)"
            class="table-toolbar"
          >
            <el-button type="text" @click="isExpand = !isExpand">
              {{ isExpand ? '收起' : '展开' }}
            </el-button>
            <el-tooltip content="刷新数据" placement="top">
              <el-button
                icon="el-icon-refresh"
                class="refresh-btn-icon"
                size="mini"
                @click="$refs.table.load(true)"
              ></el-button>
            </el-tooltip>
          </div>
          <ht-table
            v-show="isExpand"
            ref="table"
            :data="data"
            :page-result="pageResult"
            :default-querys="defaultQuery"
            :selectable="false"
            :show-export="false"
            :show-custom-column="false"
            :quick-search-props="quickSearchProp"
            :quick-search-width="quickSearchWidth"
            :nopagination="showMobileSearch"
            :no-header="
              [3, 4].includes(customChart.conf.series.showChartLayout)
            "
            custom-table-height="500"
            @load="loadData"
          >
            <template v-if="chartCountType == 0">
              <ht-table-column
                v-for="xaxisField in customChart.xaxisField"
                :key="xaxisField.field"
                :prop="xaxisField.field.toUpperCase()"
                :label="xaxisField.comment"
              ></ht-table-column>
              <ht-table-column
                v-for="displayfield in customChart.displayfield"
                :key="displayfield.field"
                :prop="displayfield.field.toUpperCase()"
                :label="displayfield.comment"
              ></ht-table-column>
            </template>
            <template v-if="chartCountType == 1">
              <ht-table-column
                v-for="(item, index) in listFields"
                :key="index"
                :prop="item.field.toUpperCase()"
                :label="item.comment"
                :width="item.width"
              ></ht-table-column>
            </template>
          </ht-table>
        </div>
      </div>
    </div>
    <div
      v-if="isMobile && permission != 'n'"
      :class="[
        fullScreen ? 'horizontal-echarts-father' : '',
        isMoreUrl ? 'mobile-chart-tabs-url' : 'mobile-chart-tabs',
      ]"
    >
      <div
        ref="tabBar"
        :class="fullScreen ? 'flex-class' : ''"
        style="position: relative"
      >
        <span v-if="fullScreen" class="chart-name">{{ name }}</span>
        <van-tabs
          v-if="
            isProcessChart ||
            (customChart.conf && customChart.conf.series.showChartType == 2)
          "
          v-model="activeName"
          title-inactive-color="#262626"
          :background="isMoreUrl ? '#fff' : '#F4F4F4'"
          title-active-color="#409eff"
          :type="isMoreUrl ? 'line' : 'card'"
          @click="initClick"
        >
          <van-tab name="查看图表" :lazy="true">
            <template #title>
              <span v-if="isMoreUrl">查看图表</span>
              <i v-else class="icon-tubiaoshitu"></i>
            </template>
          </van-tab>
          <van-tab name="查看数据" :lazy="true">
            <template #title>
              <span v-if="isMoreUrl">查看数据</span>
              <i v-else class="icon-shujushitu1"></i>
            </template>
          </van-tab>
        </van-tabs>
        <div
          class="full-screen-icon"
          :style="{
            top: isMoreUrl ? '10px' : '2px',
            right: isMoreUrl ? '22px' : '2px',
          }"
        >
          <i
            v-if="!fullScreen"
            class="icon-quanping"
            @click="openFullScreen"
          ></i>
        </div>
      </div>
      <div v-if="showMobileSearch" class="mobile-quick-search">
        <van-search
          v-model="mobileSearchVal"
          placeholder="请输入搜索内容"
          @input="handleMobileSearch"
        />
      </div>
      <div class="content-container">
        <div
          v-if="
            (showTable &&
              activeName == '查看图表' &&
              customChart.conf &&
              customChart.conf.series.showChartType != 3) ||
            isProcessChart
          "
          :id="chartKey"
          :key="chartKey"
          :ref="`${chartKey}myChart`"
          class="mobile-chart-fixed-height"
          :class="[
            fullScreen ? 'full-screen-chart' : 'chart-container',
            'vertical-chart',
          ]"
        ></div>
        <div
          v-if="isProcessChart && activeName == '查看数据'"
          class="mobile-list-auto-height"
        >
          <div ref="listContainer" class="mobile-chart-list">
            <van-empty
              v-if="getProcessChartData.length === 0 && !listLoading"
              description="暂无统计数据"
              image="empty"
            ></van-empty>
            <van-list
              v-model="listLoading"
              :finished="listFinished"
              finished-text="已加载全部数据"
              error-text="加载失败，点击重试"
              :error.sync="listError"
              offset="200"
              class="chart-data-list"
              @load="onListLoad"
            >
              <van-cell-group border>
                <van-cell
                  v-for="(item, idx) in getProcessChartData"
                  :key="idx"
                  class="list-item-row"
                  @click.stop
                >
                  <div
                    class="list-item-content"
                    :class="
                      expandedRows[idx] ? 'list-item-content-expanded' : ''
                    "
                  >
                    <div class="list-item-col">
                      <span class="list-item-col-label">X轴：</span>
                      <span class="list-item-col-value">
                        {{ item.xAxisVal || '-' }}
                      </span>
                    </div>
                    <div
                      v-for="(seriesItem, sIdx) in item.seriesList"
                      :key="sIdx"
                      class="list-item-col"
                    >
                      <span class="list-item-col-label">
                        {{ seriesItem.name }}：
                      </span>
                      <span class="list-item-col-value">
                        {{ seriesItem.value || '-' }}
                      </span>
                    </div>
                  </div>
                  <div
                    class="toggle-expand-btn"
                    @click.stop="toggleRowExpand(idx)"
                  >
                    {{ expandedRows[idx] ? '收起' : '展开' }}
                    <i
                      :class="
                        expandedRows[idx] ? 'icon-arrow-up' : 'icon-arrow-down'
                      "
                    ></i>
                  </div>
                </van-cell>
              </van-cell-group>
            </van-list>
          </div>
        </div>
        <div
          v-if="
            (!isProcessChart &&
              activeName == '查看数据' &&
              customChart.conf &&
              customChart.conf.series.showChartType == 2) ||
            (customChart.conf && customChart.conf.series.showChartType == 3)
          "
          style="height: 100%"
        >
          <div
            v-if="showTable && !isProcessChart"
            ref="listContainer"
            class="mobile-chart-list"
          >
            <van-empty
              v-if="data.length === 0 && !listLoading"
              description="暂无统计数据"
              image="empty"
            ></van-empty>
            <van-list
              v-model="listLoading"
              :finished="listFinished"
              finished-text="已加载全部数据"
              error-text="加载失败，点击重试"
              :error.sync="listError"
              offset="200"
              class="chart-data-list"
              @load="onListLoad"
            >
              <van-cell-group border>
                <van-cell
                  v-for="(item, idx) in data"
                  :key="idx"
                  class="list-item-row"
                  @click.stop
                >
                  <div
                    class="list-item-content"
                    :class="
                      expandedRows[idx] ? 'list-item-content-expanded' : ''
                    "
                  >
                    <template v-if="chartCountType == 0">
                      <div
                        v-for="xaxisField in customChart.xaxisField"
                        :key="xaxisField.field"
                        class="list-item-col"
                      >
                        <span class="list-item-col-label">
                          {{ xaxisField.comment }}：
                        </span>
                        <span class="list-item-col-value">
                          {{ item[xaxisField.field.toUpperCase()] || '-' }}
                        </span>
                      </div>
                      <div
                        v-for="displayfield in customChart.displayfield"
                        :key="displayfield.field"
                        class="list-item-col"
                      >
                        <span class="list-item-col-label">
                          {{ displayfield.comment }}：
                        </span>
                        <span class="list-item-col-value">
                          {{ item[displayfield.field.toUpperCase()] || '-' }}
                        </span>
                      </div>
                    </template>
                    <template v-if="chartCountType == 1">
                      <div
                        v-for="(fieldItem, fIdx) in listFields"
                        :key="fIdx"
                        class="list-item-col"
                      >
                        <span class="list-item-col-label">
                          {{ fieldItem.comment }}：
                        </span>
                        <span class="list-item-col-value">
                          {{ item[fieldItem.field.toUpperCase()] || '-' }}
                        </span>
                      </div>
                    </template>
                  </div>
                  <div
                    class="toggle-expand-btn"
                    @click.stop="toggleRowExpand(idx)"
                  >
                    {{ expandedRows[idx] ? '收起' : '展开' }}
                    <i
                      :class="
                        expandedRows[idx] ? 'icon-arrow-up' : 'icon-arrow-down'
                      "
                    ></i>
                  </div>
                </van-cell>
              </van-cell-group>
            </van-list>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
  import { decode } from '@/util/base64'
  import utils from '@/utils.js'
  import permission from '@/mixins/permission.js'
  import mobileMode from '@/mixins/mobileMode.js'
  export default {
    name: 'Chart',
    mixins: [permission, mobileMode],
    props: {
      id: {
        type: [String, Number],
        required: true,
      },
      alias: {
        type: [String, Number],
        default: '',
      },
      width: {
        type: [String, Number],
        default: '',
      },
      height: {
        type: [String, Number],
        default: '',
      },
      selectList: {
        type: String,
        default: '',
      },
      chartKey: {
        type: [String, Number],
        required: true,
      },
      fullScreen: {
        type: Boolean,
        default: false,
      },
      isMoreUrl: {
        type: Boolean,
        default: false,
      },
      option: {
        type: Object,
        default: () => ({}),
      },
      isProcessChart: {
        type: Boolean,
        default: false,
      },
      name: {
        type: String,
        default: '',
      },
      popupCountQuery: {
        type: Object,
        default: () => ({}),
      },
    },
    data() {
      return {
        showTable: false,
        customChart: {},
        pageResult: {
          page: 1,
          pageSize: 20,
          total: 0,
        },
        data: [],
        quickSearchProp: '',
        defaultQuery: [],
        queryOpTransObj: {
          EQ: 'EQUAL',
          BETWEEN: 'BETWEEN',
          GE: 'GREAT_EQUAL',
          LE: 'LESS_EQUAL',
          LK: 'LIKE',
          IN: 'IN',
        },
        querrySelectList: [],
        myChart: null,
        isDark: false,
        dynamicInterval: null,
        quickSearchWidth: 366,
        activeName: '查看图表',
        countQueryFileds: [],
        chartCountType: 0,
        countFields: [],
        countTypes: [
          { label: '计数', value: 'count' },
          { label: '累加', value: 'sum' },
          { label: '平均值', value: 'avg' },
        ],
        countQuery: {},
        listFields: [],
        showCountField: false,
        showCountType: false,
        showCalcField: false,
        showType: false,
        show: false,
        orderTypeSelect: false,
        dropdownActive: false,
        isExpand: true,
        timer: null,
        listLoading: false,
        listFinished: false,
        listError: false,
        initDataTimer: null,
        resizeTimer: null,
        mobileSearchVal: '',
        quickSearchInput: null,
        expandedRows: [],
        rootEventIds: [],
        defaultFormat: 'yyyy-MM-dd HH:mm:ss',
      }
    },
    computed: {
      charWidth() {
        if (this.width) {
          return `${this.width}px`
        }
        return '100%'
      },
      charHeight() {
        if (this.height) {
          return `${this.height}px`
        }
        return this.customChart.height ? `${this.customChart.height}px` : '100%'
      },
      mobileCountFields() {
        return this.countFields.map((item) => ({
          value: item.field,
          text: item.comment,
        }))
      },
      chartStyle() {
        let defaultStyle = {
          display: 'flex',
          width: '100%',
          overflow: 'auto',
          gap: '32px',
        }
        const series =
          this.customChart &&
          this.customChart.conf &&
          this.customChart.conf.series
            ? this.customChart.conf.series
            : {}
        if (series.showChartType === 2) {
          const styleData = {
            1: { justifyContent: 'space-between' },
            2: { flexFlow: 'row-reverse', justifyContent: 'space-between' },
            3: { flexDirection: 'column' },
            4: { flexFlow: 'column-reverse' },
          }
          defaultStyle = {
            ...defaultStyle,
            ...(styleData[series.showChartLayout] || {}),
          }
        }
        return defaultStyle
      },
      showColorModeType() {
        if (this.isProcessChart) {
          const colorModeType =
            this.option && this.option.colorModeType
              ? this.option.colorModeType
              : ''
          return ['dark,default', 'default,dark'].includes(colorModeType)
        } else {
          const colorModeType =
            this.customChart &&
            this.customChart.conf &&
            this.customChart.conf.colorModeType
              ? this.customChart.conf.colorModeType
              : ''
          return ['dark,default', 'default,dark'].includes(colorModeType)
        }
      },
      getProcessChartData() {
        const resData = []
        if (
          !this.option ||
          !Array.isArray(this.option.xAxis) ||
          !Array.isArray(this.option.series) ||
          this.option.xAxis.length === 0
        ) {
          return resData
        }
        const xAxisList = this.option.xAxis
        const seriesList = this.option.series
        xAxisList.forEach((xVal, idx) => {
          const rowItem = {
            xAxisVal: xVal || '-',
            seriesList: [],
          }
          seriesList.forEach((item) => {
            if (
              Array.isArray(item.data) &&
              idx < item.data.length &&
              item.data[idx] !== undefined &&
              item.data[idx] !== null
            ) {
              rowItem.seriesList.push({
                name: item.name || '未命名',
                value: item.data[idx],
              })
            }
          })
          if (
            rowItem.seriesList.length > 0 &&
            !resData.some((item) => item.xAxisVal === xVal)
          ) {
            resData.push(rowItem)
          }
        })
        return resData
      },
      nopagination() {
        const needPage =
          this.customChart &&
          this.customChart.conf &&
          this.customChart.conf.series &&
          this.customChart.conf.series.needPage
            ? this.customChart.conf.series.needPage
            : false
        return this.chartCountType === 0 && !needPage
      },
      mobileNeedPagination() {
        return !this.nopagination
      },
      showMobileSearch() {
        return this.quickSearchProp && this.quickSearchProp.trim() !== ''
      },
    },
    watch: {
      id: {
        handler(newV) {
          if (newV) {
            this.resetChartData()
            this.initData()
          }
        },
        immediate: false,
      },
      alias: {
        handler(newV) {
          if (newV) {
            this.resetChartData()
            this.initData()
          }
        },
        immediate: false,
      },
      'countQuery.countField': {
        handler() {
          this.initClick()
        },
        immediate: false,
      },
      'customChart.conf.colorModeType': {
        handler(newV) {
          this.isDark = newV === 'dark'
        },
        deep: true,
        immediate: true,
      },
      'option.colorModeType': {
        handler(newV) {
          if (this.isProcessChart) {
            this.isDark = newV === 'dark'
          }
        },
        deep: true,
        immediate: true,
      },
      isDark: {
        handler() {
          this.modeChange()
        },
        immediate: false,
      },
    },
    created() {
      const collapseHandler = () => {
        this.$nextTick(() => this.resizeChart())
      }
      const tabClickHandler = () => {
        this.$nextTick(() => this.resizeChart())
      }
      this.rootEventIds.push({
        name: 'collapse-active-names',
        handler: collapseHandler,
      })
      this.rootEventIds.push({ name: 'tab-click', handler: tabClickHandler })
      this.$root.$on('collapse-active-names', collapseHandler)
      this.$root.$on('tab-click', tabClickHandler)
    },
    mounted() {
      this.countQuery = this.fullScreen
        ? { ...this.popupCountQuery }
        : { ...this.countQuery }
      this.$nextTick(() => {
        this.initData()
      })
      if (this.selectList) {
        try {
          this.querrySelectList = JSON.parse(decode(this.selectList))
          const pInst = utils.getOnlineFormInstance(this)
          this.querrySelectList.forEach((obj) => {
            const path = `data.${obj.selectField}`
            pInst.$watch(path, () => {
              this.resetChartData()
              this.$nextTick(() => {
                this.initData()
              })
            })
          })
        } catch (e) {
          console.error('解析selectList失败：', e)
          this.querrySelectList = []
        }
      }
      this.$watch('activeName', () => {
        this.calcListHeight()
        this.resizeChart()
      })
      window.addEventListener('resize', this.handleWindowResize)
    },
    beforeUnmount() {
      window.removeEventListener('resize', this.handleWindowResize)
      this.rootEventIds.forEach(({ name, handler }) => {
        this.$root.$off(name, handler)
      })
      this.rootEventIds = []
      this.clearAllTimers()
      if (this.myChart) {
        this.myChart.dispose()
        this.myChart = null
      }
    },
    methods: {
      initEchartsInstance() {
        const chartDom = this.$refs[`${this.chartKey}myChart`]
        if (!chartDom) {
          console.warn('ECharts 初始化失败：未找到图表容器 DOM', this.chartKey)
          return null
        }
        const targetDom = Array.isArray(chartDom) ? chartDom[0] : chartDom
        if (!targetDom || !targetDom.nodeType) {
          console.warn('ECharts 初始化失败：无效的 DOM 节点', targetDom)
          return null
        }
        if (this.myChart) {
          this.myChart.dispose()
          this.myChart = null
        }
        return this.$echarts.init(targetDom, this.isDark ? 'dark' : 'default')
      },

      resetChartData() {
        this.customChart = {}
        this.data = []
        this.quickSearchProp = ''
        this.defaultQuery = []
        this.showTable = false
      },
      clearAllTimers() {
        this.dynamicInterval && clearInterval(this.dynamicInterval)
        this.timer && clearInterval(this.timer)
        this.resizeTimer && clearTimeout(this.resizeTimer)
        this.initDataTimer && clearTimeout(this.initDataTimer)
        this.quickSearchInput && clearTimeout(this.quickSearchInput)

        this.dynamicInterval = null
        this.timer = null
        this.resizeTimer = null
        this.initDataTimer = null
        this.quickSearchInput = null
      },
      handleWindowResize() {
        clearTimeout(this.resizeTimer)
        this.resizeTimer = setTimeout(() => {
          this.calcListHeight()
          this.resizeChart()
        }, 100)
      },
      onConfirmCountField(value) {
        this.countQuery.countField = value.field
        this.showCountField = false
        this.initClick()
      },
      onConfirmCountType(value) {
        this.countQuery.countType = value.value
        this.showCountType = false
      },
      onConfirmCalcField(value) {
        this.countQuery.calcField = value.field
        this.showCalcField = false
      },
      getCountField() {
        const selectedOption = this.countFields.find(
          (item) => item.field === this.countQuery.countField
        )
        return selectedOption && selectedOption.comment
          ? selectedOption.comment
          : null
      },
      getCalcField() {
        const selectedOption = this.countFields.find(
          (item) => item.field === this.countQuery.calcField
        )
        return selectedOption && selectedOption.comment
          ? selectedOption.comment
          : null
      },
      getCountType() {
        const selectedOption = this.countTypes.find(
          (item) => item.value === this.countQuery.countType
        )
        return selectedOption && selectedOption.label
          ? selectedOption.label
          : null
      },
      resizeChart() {
        if (this.myChart && typeof this.myChart.resize === 'function') {
          this.myChart.resize()
        }
      },
      initClick(tabVue) {
        this.$nextTick(() => {
          this.initData()
          this.calcListHeight()
        })
      },
      loadData(param, cb) {
        if (this.customChart.alias) {
          if (this.nopagination) {
            if (
              this.customChart.conf &&
              this.customChart.conf.maxLength &&
              this.customChart.conf.maxLength !== '0'
            ) {
              param.pageBean.pageSize = parseInt(
                this.customChart.conf.maxLength
              )
            } else {
              param.pageBean.pageSize = -1
            }
          }
          const pInst = utils.getOnlineFormInstance(this)
          let conditions = this.customChart.conditionfield || []
          let querrySelectList = this.querrySelectList
          let queryFilter = param && param.querys ? param.querys : []
          querrySelectList.forEach((item) => {
            let value = utils.getValueByPath(pInst.data, item.selectField)
            let query = conditions.find((obj) => obj.field == item.bindSelectd)
            let valueComputed = '' //输入值或默认值
            if (!utils.isEmpty(value)) {
              valueComputed = value
            } else if (!utils.isEmpty(item.defaultValue)) {
              valueComputed = item.defaultValue
            }

            if (!utils.isEmpty(valueComputed) && query) {
              queryFilter.push({
                property: query.field,
                value: valueComputed,
                operation: this.queryOpTransObj[query.condition],
                group: 'main',
              })
            }
          })
          if (this.chartCountType === 1) {
            this.countQueryFileds.forEach((e) => {
              if (this.countQuery[e.field]) {
                if (this.countQuery[e.field].indexOf(',') !== -1) {
                  this.countQuery[e.field].split(',').forEach((value) => {
                    queryFilter.push({
                      group: e.field,
                      operation: this.queryOpTransObj[e.condition],
                      property: e.field,
                      relation: 'OR',
                      value: value,
                    })
                  })
                } else {
                  queryFilter.push({
                    group: 'main',
                    operation: this.queryOpTransObj[e.condition],
                    property: e.field,
                    relation: 'AND',
                    value: this.countQuery[e.field],
                  })
                }
              }
            })
            if (['sum', 'avg'].includes(this.countQuery.countType)) {
              let calcFieldObj = this.countFields.find(
                (e) => e.field === this.countQuery.calcField
              )
              if (!calcFieldObj) {
                const msg = '无法统计，请选择计算字段！'
                if (this.isMobile) {
                  this.$notify({ type: 'warning', message: msg })
                } else {
                  this.$message.error(msg)
                }
                if (cb) cb()
                return false
              }
              if (calcFieldObj.dbType !== 'number') {
                const msg = '无法统计，请调整统计策略！'
                if (this.isMobile) {
                  this.$notify({ type: 'warning', message: msg })
                } else {
                  this.$message.error(msg)
                }
                if (cb) cb()
                return false
              }
            }
          }
          if (this.defaultQuery && this.defaultQuery.length > 0) {
            param.querys.push(...this.defaultQuery)
          }
          param.querys = queryFilter
          this.$requestConfig
            .getChartListData(
              this.customChart.alias,
              param,
              this.countQuery.countField,
              this.countQuery.countType,
              this.countQuery.calcField
            )
            .then((response) => {
              let countData
              let listData
              if (this.chartCountType === 0) {
                if (
                  Object.prototype.toString.call(response) === '[object Object]'
                ) {
                  response.rows.forEach((item) => {
                    this.keyUpper(item)
                  })
                  countData = response
                  listData = response
                } else {
                  response.forEach((item) => {
                    this.keyUpper(item)
                  })
                  countData = response
                  listData = response
                }
              } else {
                response.listData.rows.forEach((item) => {
                  this.keyUpper(item)
                })
                listData = response.listData
                response.countData.rows.forEach((item) => {
                  this.keyUpper(item)
                })
                countData = response.countData
              }
              this.pageResult = {
                page: listData.page,
                pageSize: listData.pageSize,
                total: listData.total,
              }
              this.data = listData.rows
              this.customChart.rows = countData.rows
              this.$nextTick(() => {
                this.buildChart(this.customChart)
              })
            })
            .finally(() => {
              if (cb) cb()
            })
        } else {
          if (cb) cb()
        }
      },
      initData() {
        if (this.isProcessChart) {
          this.showTable = true
          this.$nextTick(() => {
            this.myChart = this.initEchartsInstance()
            if (this.myChart) {
              let myOption = this.initProcessChart(this.option)
              console.log('myOption: ', myOption)
              this.myChart.setOption(myOption)
              this.myChart.resize()
            }
          })
        } else {
          let respPermission = null
          let pkDesc = '主键'
          let pkVal = this.id
          if (this.alias && this.$requestConfig.getChartByAlias) {
            pkDesc = '别名'
            pkVal = this.alias
            respPermission = this.$requestConfig.getChartByAlias(this.alias)
          } else {
            respPermission = this.$requestConfig.getChartById(this.id)
          }
          respPermission.then((data) => {
            if (data) {
              try {
                data.conditionfield = JSON.parse(data.conditionfield || '[]')
                data.displayfield = JSON.parse(data.displayfield || '[]')
                data.xaxisField = JSON.parse(data.xaxisField || '[]')
                data.sortfield = JSON.parse(data.sortfield || '[]')
                data.conf = JSON.parse(data.conf || '{}')
                if (data.chartCountType === 1) {
                  data.countField = JSON.parse(data.countField || '[]')
                  if (data.countField.length && !this.countQuery.countField) {
                    this.$set(
                      this.countQuery,
                      'countField',
                      data.countField[0].field
                    )
                  }
                  if (!this.countQuery.countType) {
                    this.$set(this.countQuery, 'countType', 'count')
                  }
                  this.countFields = data.countField
                  data.listField = JSON.parse(data.listField || '[]')
                  if (data.listField.length) {
                    this.$set(this, 'listFields', data.listField)
                  }
                }
                if (
                  data.chartCountType == null ||
                  data.chartCountType == undefined
                ) {
                  data.chartCountType = 0
                }
                this.chartCountType = data.chartCountType
                this.customChart = data
                this.initCondition()
                this.showTable = true
                const defaultParams = {
                  pageBean: { ...this.pageResult },
                  querys: [],
                }
                if (this.isMobile && this.nopagination) {
                  defaultParams.pageBean.pageSize = -1
                  defaultParams.pageBean.page = 1
                }

                this.loadData(defaultParams)
                if (
                  data.conf &&
                  data.conf.series &&
                  data.conf.series.refreshRate
                ) {
                  this.timer = setInterval(() => {
                    this.loadData(defaultParams)
                  }, Number(data.conf.series.refreshRate) * 1000)
                }
              } catch (e) {
                console.error('解析图表配置失败：', e)
                this.$message.error('图表配置解析异常，请检查配置格式')
              }
            } else {
              this.$message.error(`根据图表${pkDesc}[${pkVal}]未找到图表`)
            }
          })
        }
      },
      initCondition() {
        let conditions = this.customChart.conditionfield || []
        this.quickSearchProp = ''
        if (this.customChart.chartCountType === 0) {
          for (let i = 0; i < conditions.length; i++) {
            if (conditions[i].defaultType === '1') {
              this.quickSearchProp += conditions[i].field.toUpperCase() + ','
            } else {
              let value = conditions[i].defaultValue
              if (conditions[i].condition === 'BETWEEN') {
                value += ',' + conditions[i].endDate
              }
              this.defaultQuery.push({
                property: conditions[i].field,
                value: value,
                operation: this.queryOpTransObj[conditions[i].condition],
                group: 'main',
              })
            }
          }
          this.quickSearchProp = this.quickSearchProp.replace(/,$/, '')
        } else {
          this.countQueryFileds = []
          for (let i = 0; i < conditions.length; i++) {
            if (conditions[i].defaultType === '1') {
              this.countQueryFileds.push(conditions[i])
            } else {
              let value = conditions[i].defaultValue
              if (conditions[i].condition === 'BETWEEN') {
                value += ',' + conditions[i].endDate
              }
              this.defaultQuery.push({
                property: conditions[i].field,
                value: value,
                operation: this.queryOpTransObj[conditions[i].condition],
                group: 'main',
              })
            }
          }
        }
      },
      buildChart(data) {
        this.$nextTick(() => {
          this.myChart = this.initEchartsInstance()
          if (!this.myChart) return
          if (this.customChart.chartCountType === 1) {
            let countFieldObj = this.countFields.find(
              (e) => e.field === this.countQuery.countField
            )
            this.customChart.xaxisField = [countFieldObj]
            this.customChart.displayfield = [
              {
                comment: '统计值',
                dbType: 'bigint',
                field: 'CT',
                nameKey: '0',
                type:
                  countFieldObj && countFieldObj.type ? countFieldObj.type : '',
                yAxis:
                  countFieldObj && countFieldObj.yAxis
                    ? countFieldObj.yAxis
                    : 0,
              },
            ]
          }
          let grid = this.getGrid()
          data.conf.title.text = data.name
          data.conf.title.top = this.getTitlePosition()
          let option = {
            title: data.conf.title,
            tooltip: data.conf.legend.showDetail
              ? { extraCssText: 'z-index:99' }
              : false,
            toolbox: {
              feature: { saveAsImage: {} },
              show: this.isMobile ? false : true,
            },
            legend: { type: 'scroll', orient: 'horizontal', bottom: 'bottom' },
            grid: grid,
            series: {},
          }
          switch (data.style) {
            case 1:
              option = this.buildLine(data, option)
              break
            case 3:
              option = this.buildPie(data, option)
              break
            case 4:
              option = this.buildRadar(data, option)
              break
            case 5:
              option = this.buildFunnel(data, option)
              break
            case 6:
              option = this.buildScatter(data, option)
              break
            case 7:
              option = this.buildHeatmap(data, option)
              break
            default:
              break
          }
          this.adjustChatTitle(data, option)
          this.myChart.setOption(option)
          this.addDynamic(option)
        })
      },
      addDynamic(option) {
        this.dynamicInterval && clearInterval(this.dynamicInterval)
        if (!this.myChart) {
          console.warn('图表实例未初始化，无法执行动态高亮操作')
          return
        }
        let current = -1
        let index = -1
        const dataLen1 = option.series[0].data.length || 0
        const dataLen2 = option.series.length || 0
        this.dynamicInterval = setInterval(() => {
          if (!this.myChart) {
            clearInterval(this.dynamicInterval)
            this.dynamicInterval = null
            return
          }
          this.myChart.dispatchAction({
            type: 'downplay',
            seriesIndex: index,
            dataIndex: current,
          })
          current = (current + 1) % dataLen1
          if (current === 0) {
            index = (index + 1) % dataLen2
          }
          this.myChart.dispatchAction({
            type: 'highlight',
            seriesIndex: index,
            dataIndex: current,
          })
          this.myChart.dispatchAction({
            type: 'showTip',
            seriesIndex: index,
            dataIndex: current,
          })
        }, 2000)
      },
      buildLine(data, option) {
        let displayField = this.customChart.displayfield || []
        let legendData = []
        let xAxisData = []
        let seriesData = []
        let xAxisField = this.customChart.xaxisField[0] || {}
        let series = this.customChart.conf.series || {}

        for (let i = 0, d; (d = displayField[i++]); ) {
          if (d) {
            let arr = []
            let type = d.type
            legendData.push(d.comment)
            for (let j = 0, res; (res = data.rows[j++]); ) {
              if (i === 1) {
                let value = res[xAxisField.field.toUpperCase()]
                if (
                  xAxisField.dbType === 'date' &&
                  value &&
                  typeof value === 'number'
                ) {
                  value = new Date(value).format(this.defaultFormat)
                }
                xAxisData.push(value)
              }
              if (res) {
                let value = res[d.field.toUpperCase()]
                if (value && value.length >= 8) option.grid.left = '25%'
                arr.push(value)
              }
            }
            let se = {
              symbol: 'circle',
              symbolSize: 8,
              smooth: false,
              name: d.comment,
              type: d.type,
              data: arr,
              stack: series.stack ? 'one' : null,
              itemStyle: {
                normal: {
                  // color: '#409EFF',
                  lineStyle: {
                    color: '#409EFF',
                    width: 1,
                  },
                  areaStyle: {
                    //color: '#94C9EC'
                    color: this.$echarts.graphic.LinearGradient(0, 1, 0, 0, [
                      {
                        offset: 1,
                        color: '#409EFF',
                      },
                      {
                        offset: 0,
                        color: 'rgba(255,255,255,0.16)',
                      },
                    ]),
                  },
                },
              },
            }
            if (series.doubleYAxis) {
              se.yAxisIndex = d.yAxis === 1 ? 0 : 1
            }
            if (type === 'line') {
              se.smooth = true
            }
            seriesData.push(se)
          }
        }
        option.series = seriesData
        option.tooltip = data.conf.legend.showDetail
          ? {
              trigger: 'axis',
              axisPointer: {
                // 设置指示线
                type: 'line', // 默认为直线，可选为：'line' | 'shadow'
                lineStyle: {
                  color: '#E6E6E6',
                },
              },
              extraCssText: 'z-index:99',
            }
          : false
        option.legend.y = 50
        if (series.showType || !series.doubleYAxis) {
          option.xAxis = [
            {
              type: 'category',
              splitLine: { lineStyle: { type: 'dashed' } },
              data: xAxisData,
            },
          ]
          let leftY = {
            type: 'value',
            axisLine: { lineStyle: { color: '#575E71' } },
            splitLine: { lineStyle: { type: 'dashed' } },
          }

          let rightY = {
            type: 'value',
            axisLine: { lineStyle: { color: '#575E71' } },
            splitLine: { lineStyle: { type: 'dashed' } },
            position: 'right',
          }
          option.yAxis = series.doubleYAxis ? [leftY, rightY] : [leftY]
        } else {
          option.xAxis = [
            {
              type: 'value',
              splitLine: { lineStyle: { type: 'dashed' } },
            },
          ]
          option.yAxis = [
            {
              type: 'category',
              splitLine: { lineStyle: { type: 'dashed' } },
              data: xAxisData,
            },
          ]
        }

        option.yAxis[0].axisLabel = {
          color: '#444343',
          formatter: function (value) {
            // value大于1000时除以1000并拼接k，小于1000按原格式显示
            if (value >= 1000) {
              return value / 1000 + 'k'
            }
            return value
          },
        }
        //柱状图的时候两端留空。避免柱状图挡住y轴。折线图则不留空，
        if (data.style === 2) option.xAxis[0]['boundaryGap'] = true
        if (data.conf) {
          let obj = data.conf
          if (obj.yMin === 2) option.yAxis[0]['min'] = 'dataMin'
          if (obj.xShowAll === 2) {
            option.xAxis[0]['axisLabel'] = {
              interval: 0, //横轴信息全部显示
              rotate: 30,
            }
          }
        }
        if ((data.rows.length > 20 && !series.dataZoom) || this.isMobile) {
          option.dataZoom = [
            {
              type: 'slider',
              show: true,
              bottom: '30px',
              borderColor: '#07417a',
              backgroundColor: 'transparent',
              dataBackground: {
                lineStyle: {
                  color: 'transparent',
                  shadowOffsetY: 0,
                },
                areaStyle: {
                  color: 'transparent',
                  shadowOffsetY: 0,
                },
              },
              // 拖拽手柄样式 svg 路径
              handleIcon:
                'M512 512m-208 0a6.5 6.5 0 1 0 416 0 6.5 6.5 0 1 0-416 0Z M512 192C335.264 192 192 335.264 192 512c0 176.736 143.264 320 320 320s320-143.264 320-320C832 335.264 688.736 192 512 192zM512 800c-159.072 0-288-128.928-288-288 0-159.072 128.928-288 288-288s288 128.928 288 288C800 671.072 671.072 800 512 800z',
              handleColor: '#aab6c6',
              height: 6,
              handleSize: 12,
              showDataShadow: false,
              filterMode: 'filter',
              textStyle: { color: '#ccc' },
              start: 0,
              end: 100,
            },
          ]
          option.grid.bottom = 50
        } else {
          delete option.dataZoom
        }
        option.legend = {
          top: 10,
          itemGap: 50,
          data: legendData,
          ...data.conf.legend,
        }
        return option
      },
      buildPie(data, option) {
        let displayField = data.displayfield || []
        let legendData = []
        let seriesData = []
        let xAxisField = data.xaxisField[0] || {}
        let series = data.conf.series || {}
        const appendDisplayData = data.conf.appendDisplayData || 'none'
        if (series.showType) {
          let duration = 100 / (data.rows.length || 1)
          for (let j = 0; j < data.rows.length; j++) {
            let value = []
            for (let i = 0; i < displayField.length; i++) {
              value.push({
                name: displayField[i].comment,
                value: data.rows[j][displayField[i].field.toUpperCase()],
              })
              if (j === 0) {
                legendData.push(displayField[i].comment)
              }
            }
            seriesData.push({
              name: data.rows[j][xAxisField.field.toUpperCase()],
              type: 'pie',
              data: value,
              radius: [series.radius[0] + '%', series.radius[1] + '%'],
              center: [
                Number(series.center[0]) + j * duration + '%',
                series.center[1] + '%',
              ],
              roseType: series.roseType,
              selectedMode: series.selectedMode,
              label: { position: series.label.position },
              itemStyle: {
                emphasis: {
                  shadowBlur: 10,
                  shadowOffsetX: 0,
                  shadowColor: 'rgba(0, 0, 0, 0.5)',
                },
              },
            })
          }
        } else {
          let duration = 100 / (displayField.length || 1)
          for (let i = 0; i < displayField.length; i++) {
            let value = []
            const sum = data.rows.reduce((pre, cur) => {
              return pre + cur[displayField[i].field.toUpperCase()]
            }, 0)

            for (let j = 0; j < data.rows.length; j++) {
              const itemData =
                appendDisplayData === 'show'
                  ? `：${data.rows[j][displayField[i].field.toUpperCase()]} (${(
                      (data.rows[j][displayField[i].field.toUpperCase()] /
                        sum) *
                      100
                    ).toFixed(2)}%)`
                  : ''
              value.push({
                name: data.rows[j][xAxisField.field.toUpperCase()] + itemData,
                value: data.rows[j][displayField[i].field.toUpperCase()],
              })
              if (i === 0) {
                legendData.push(
                  data.rows[j][xAxisField.field.toUpperCase()] + ''
                )
              }
            }
            seriesData.push({
              name: displayField[i].comment,
              type: 'pie',
              data: value,
              radius: [series.radius[0] + '%', series.radius[1] + '%'],
              center: [
                Number(series.center[0]) + i * duration + '%',
                series.center[1] + '%',
              ],
              roseType: series.roseType,
              selectedMode: series.selectedMode,
              label: { position: series.label.position },
              itemStyle: {
                emphasis: {
                  shadowBlur: 10,
                  shadowOffsetX: 0,
                  shadowColor: 'rgba(0, 0, 0, 0.5)',
                },
              },
            })
          }
        }

        option.series = seriesData
        option.tooltip = data.conf.legend.showDetail
          ? {
              extraCssText: 'z-index:99',
              trigger: 'item',
              formatter:
                appendDisplayData === 'show'
                  ? '{a} <br/>{b}'
                  : '{a} <br/>{b} : {c} ({d}%)',
            }
          : false

        option.legend = {
          orient: 'horizontal',
          bottom: 5,
          textStyle: { fontSize: 16 },
          formatter(name) {
            const itemName = name.split('：')[0]
            return `${itemName}`
          },
          ...data.conf.legend,
        }
        return option
      },
      buildRadar(data, option) {
        let displayField = data.displayfield || []
        let legendData = []
        let xAxisField = data.xaxisField[0] || {}
        let arr = []
        let seriesData = []
        let indicator = []
        let series = data.conf.series || {}

        if (series.showType) {
          for (let j = 0; j < data.rows.length; j++) {
            let value = []
            for (let i = 0; i < displayField.length; i++) {
              value.push(data.rows[j][displayField[i].field.toUpperCase()])
              if (j === 0) {
                indicator.push({ name: displayField[i].comment })
              }
            }
            legendData.push(data.rows[j][xAxisField.field.toUpperCase()] + '')
            arr.push({
              name: data.rows[j][xAxisField.field.toUpperCase()],
              value: value,
              areaStyle: { opacity: '0' },
            })
          }
          seriesData = [
            {
              type: 'radar',
              data: arr,
              itemStyle: {
                emphasis: {
                  areaStyle: { opacity: series.areaStyle ? '0' : '0.5' },
                },
              },
              areaStyle: {
                color: {
                  type: 'radial',
                  colorStops: [
                    { offset: 0, color: 'rgba(137, 190, 252, 0.00)' },
                    { offset: 1, color: 'rgba(137, 190, 252, 0.5)' },
                  ],
                },
              },
            },
          ]
        } else {
          for (let i = 0; i < displayField.length; i++) {
            let value = []
            for (let j = 0; j < data.rows.length; j++) {
              value.push(data.rows[j][displayField[i].field.toUpperCase()])
              if (i === 0) {
                indicator.push({
                  name: data.rows[j][xAxisField.field.toUpperCase()],
                })
              }
            }
            legendData.push(displayField[i].comment)
            arr.push({
              name: displayField[i].comment,
              value: value,
              areaStyle: {
                color: {
                  type: 'radial',
                  colorStops: [
                    { offset: 0, color: 'rgba(137, 190, 252, 0.00)' },
                    { offset: 1, color: 'rgba(137, 190, 252, 1)' },
                  ],
                },
              },
            })
          }
          seriesData = [
            {
              type: 'radar',
              data: arr,
              itemStyle: {
                emphasis: {
                  areaStyle: { opacity: series.areaStyle ? '0' : '0.5' },
                },
              },
              areaStyle: {
                color: {
                  type: 'radial',
                  colorStops: [
                    { offset: 0, color: 'rgba(137, 190, 252, 0.00)' },
                    { offset: 1, color: 'rgba(81, 137, 248, 0.5)' },
                  ],
                },
              },
            },
          ]
        }

        let newArr = arr.map((item) => item.value).flat()
        let arrMax = Math.max(...newArr.map(Number))
        let indicatorArr = indicator.map((item) => ({ ...item, max: arrMax }))

        option.series = seriesData
        option.radar = {
          axisName: { fontSize: 14, color: '#6D7278' },
          axisLine: { lineStyle: { color: '#ebeef3' } },
          shape: 'circle',
          triggerEvent: false,
          center: [series.center[0] + '%', series.center[1] + '%'],
          radius: series.radius,
          indicator: indicatorArr,
        }
        option.legend = { ...data.conf.legend, data: legendData }
        return option
      },
      buildFunnel(data, option) {
        let displayField = data.displayfield || []
        let legendData = []
        let xAxisField = data.xaxisField[0] || {}
        let seriesData = []
        let series = data.conf.series || {}

        if (series.showType) {
          let duration = 100 / (data.rows.length || 1)
          for (let j = 0; j < data.rows.length; j++) {
            let value = []
            for (let i = 0; i < displayField.length; i++) {
              value.push({
                name: displayField[i].comment,
                value: data.rows[j][displayField[i].field.toUpperCase()],
              })
              if (j === 0) {
                legendData.push(displayField[i].comment)
              }
            }
            seriesData.push({
              name: data.rows[j][xAxisField.field.toUpperCase()],
              type: 'funnel',
              data: value,
              width: duration - 5 + '%',
              left: j * duration + '%',
              funnelAlign: series.funnelAlign,
              sort: series.sort,
              label: { position: series.label.position },
            })
          }
        } else {
          let duration = 100 / (displayField.length || 1)
          for (let i = 0; i < displayField.length; i++) {
            let value = []
            for (let j = 0; j < data.rows.length; j++) {
              value.push({
                name: data.rows[j][xAxisField.field.toUpperCase()],
                value: data.rows[j][displayField[i].field.toUpperCase()],
              })
              if (i === 0) {
                legendData.push(
                  data.rows[j][xAxisField.field.toUpperCase()] + ''
                )
              }
            }
            seriesData.push({
              name: displayField[i].comment,
              type: 'funnel',
              data: value,
              width: duration - 5 + '%',
              left: i * duration + '%',
              funnelAlign: series.funnelAlign,
              sort: series.sort,
              label: { position: series.label.position },
            })
          }
        }

        option.series = seriesData
        option.legend = {
          ...data.conf.legend,
          type: 'scroll',
          orient: 'horizontal',
          bottom: 'bottom',
          data: legendData,
        }
        return option
      },
      buildScatter(data, option) {
        let displayField = data.displayfield || []
        let legendData = []
        let xAxisData = []
        let xAxisField = data.xaxisField[0] || {}
        let seriesData = []
        let series = data.conf.series || {}

        if (series.showType) {
          for (let j = 0; j < data.rows.length; j++) {
            let value = []
            for (let i = 0; i < displayField.length; i++) {
              if (j === 0) {
                xAxisData.push(displayField[i].comment)
              }
              value.push([
                displayField[i].comment,
                data.rows[j][displayField[i].field.toUpperCase()],
              ])
            }
            legendData.push(data.rows[j][xAxisField.field.toUpperCase()] + '')
            seriesData.push({
              name: data.rows[j][xAxisField.field.toUpperCase()],
              type: 'scatter',
              data: value,
              symbolSize() {
                return 30
              },
              itemStyle: {
                shadowBlur: 10,
                shadowColor: 'rgba(77,76,76,0.5)',
                shadowOffsetY: 5,
              },
            })
          }
        } else {
          for (let i = 0; i < displayField.length; i++) {
            let value = []
            for (let j = 0; j < data.rows.length; j++) {
              if (i === 0) {
                xAxisData.push(
                  data.rows[j][xAxisField.field.toLocaleUpperCase()]
                )
              }
              value.push([
                data.rows[j][xAxisField.field.toLocaleUpperCase()],
                data.rows[j][displayField[i].field.toLocaleUpperCase()],
              ])
            }
            legendData.push(displayField[i].comment)
            seriesData.push({
              name: displayField[i].comment,
              type: 'scatter',
              data: value,
              symbolSize() {
                return 30
              },
              itemStyle: {
                shadowBlur: 10,
                shadowColor: 'rgba(77,76,76,0.5)',
                shadowOffsetY: 5,
              },
            })

            if (
              displayField[i].regression &&
              displayField[i].regression !== 'none'
            ) {
              let myRegression = this.$ecStat.regression(
                displayField[i].regression,
                value
              )
              myRegression.points.sort((a, b) => a[0] - b[0])
              seriesData.push({
                type: 'line',
                showSymbol: false,
                data: myRegression.points,
                markPoint: {
                  itemStyle: { color: 'transparent' },
                  label: {
                    show: true,
                    position: 'left',
                    formatter: myRegression.expression,
                    color: '#333',
                    fontSize: 14,
                  },
                  data: [
                    {
                      coord:
                        myRegression.points[myRegression.points.length - 1],
                    },
                  ],
                },
              })
            }
          }
        }

        option.series = seriesData
        option.xAxis = {
          type: data.conf.series.xAxisType,
          splitLine: { lineStyle: { type: 'dashed' } },
        }

        if (data.conf.series.xAxisType === 'category') {
          option.xAxis.data = xAxisData
        }

        option.yAxis = {
          splitLine: { lineStyle: { type: 'dashed' } },
          scale: true,
        }

        option.legend = { ...data.conf.legend, data: legendData }
        return option
      },
      buildHeatmap(data, option) {
        let displayField = data.displayfield || []
        let legendData = []
        let rows = []
        let columns = []
        let arr = []
        let max = 0
        let xAxisField = data.xaxisField[0] || {}

        for (let i = 0; i < data.rows.length; i++) {
          for (let j = 0; j < displayField.length; j++) {
            if (i === 0) {
              columns.push(displayField[j].comment)
            }
            const field = data.rows[i][displayField[j].field.toUpperCase()] || 0
            const num = /[\u4e00-\u9fa5]/gm.test(field) ? 0 : field
            arr.push([i, j, num])
            if (Number(num) > max) {
              max = num
            }
          }
          rows.push(data.rows[i][xAxisField.field.toUpperCase()])
        }

        option.series = [
          {
            type: 'heatmap',
            data: arr,
            label: { normal: { show: true } },
          },
        ]

        option.xAxis = {
          type: 'category',
          data: rows,
          splitArea: { show: true },
        }

        option.yAxis = {
          type: 'category',
          data: columns,
          splitArea: { show: true },
        }

        option.visualMap = {
          show: false,
          color: ['#0d59b7', '#bee8ff'],
          min: 0,
          max: max,
          calculable: true,
          orient: 'horizontal',
          left: 'center',
          bottom: '0%',
        }

        option.tooltip = data.conf.legend.showDetail
          ? { position: 'top', extraCssText: 'z-index:99' }
          : false

        option.legend = { ...data.conf.legend, data: legendData }
        return option
      },
      getGrid() {
        let top = 0
        let bottom = 0
        let grid = {}

        if (this.isMobile) {
          this.customChart.conf.title.show = false
        }

        if (this.customChart.conf.title.show) {
          if (this.customChart.conf.title.top === 'top') {
            top += 7
            if (this.customChart.conf.title.subtext) {
              top += 5
            }
          } else if (this.customChart.conf.title.top === 'bottom') {
            bottom += 16
          }
        }

        if (this.customChart.conf.legend.show) {
          if (this.customChart.conf.legend.top === 'top') {
            top += 7
          } else if (this.customChart.conf.legend.top === 'bottom') {
            bottom += 10
          }
        }

        grid.top = top !== 0 ? `${top}%` : '5%'
        if (bottom !== 0) {
          grid.bottom = `${bottom}%`
        }
        grid.left = '10%'
        return grid
      },
      getTitlePosition() {
        if (this.customChart.conf.title.top === 'top') {
          return this.customChart.conf.legend.show &&
            this.customChart.conf.legend.top === 'top'
            ? '20'
            : 'auto'
        } else if (this.customChart.conf.title.top === 'bottom') {
          return this.customChart.conf.legend.show &&
            this.customChart.conf.legend.top === 'bottom'
            ? '84%'
            : '88%'
        } else {
          return this.customChart.conf.title.top
        }
      },
      adjustChatTitle(data, option) {
        option.legend.top = 30
        option.grid.top = 80

        if (data.conf && data.conf.title && data.conf.title.subtext) {
          option.legend.top = 50
          option.grid.top = 100
        }

        if (this.isMobile) {
          option.grid.top = 50
          option.grid.bottom = 60
          option.legend.top = 'bottom'
          if (data.conf && data.conf.title && data.conf.title.subtext) {
            option.grid.top = 80
          }
        }
      },
      modeChange() {
        if (this.isProcessChart) {
          this.showTable = true
          this.$nextTick(() => {
            this.myChart && this.myChart.dispose()
            this.myChart = this.$echarts.init(
              this.$refs[`${this.chartKey}myChart`],
              this.isDark ? 'dark' : 'default'
            )
            let myOption = this.initProcessChart(this.option)
            this.myChart.setOption(myOption)
            this.myChart.resize()
          })
        } else {
          this.$nextTick(() => {
            this.buildChart(this.customChart)
          })
        }
      },
      optionToContent(opt) {
        let axisData = opt.xAxis
        let series = opt.series
        let tdHeaders = '<th>xAxis</th>'

        series.forEach(function (item) {
          tdHeaders += `<th>${item.name}</th>`
        })

        let table = `<div class="table-responsive"><table border="0" class="bordered" style="text-align:center"><tbody><tr>${tdHeaders}</tr>`
        let tdBodys = ''

        if (axisData && axisData.length) {
          for (let i = 0, l = axisData.length; i < l; i++) {
            for (let j = 0; j < series.length; j++) {
              tdBodys += `<td>${series[j].data[i]}</td>`
            }
            table += `<tr><td style="padding: 0 10px">${axisData[i]}</td>${tdBodys}</tr>`
            tdBodys = ''
          }
        }

        table += '</tbody></table></div>'
        return table
      },
      openFullScreen() {
        this.$emit('openFullScreen', this.countQuery)
      },
      initProcessChart(option) {
        var dataZoomEnd = option.xAxis && option.xAxis.length > 2 ? 30 : 100
        let myOption = {
          tooltip: {
            extraCssText: 'z-index:99',
            trigger: 'axis',
            axisPointer: { type: 'shadow' },
            confine: true,
          },
          toolbox: {
            show: this.isMobile ? false : true,
            feature: {
              saveAsImage: {
                show: true,
                excludeComponents: ['toolbox'],
                pixelRatio: 2,
              },
            },
          },
          title: { subtext: option.subtext, top: 20 },
          legend: { type: 'scroll', orient: 'horizontal', bottom: 'bottom' },
          xAxis: {
            show: option.series[0].type == 'pie' ? false : true,
            type: 'category',
            axisTick: { alignWithLabel: true },
            data: option.xAxis,
          },
          yAxis: {
            type: 'value',
            show: option.series[0].type == 'pie' ? false : true,
            axisLabel: {
              color: '#444343',
              formatter: function (value) {
                if (value >= 1000) {
                  return value / 1000 + 'k'
                }
                return value
              },
            },
          },
          grid: { left: 35 },
          dataZoom: [
            {
              type: 'slider',
              show: dataZoomEnd == 100 ? false : true,
              bottom: '40px',
              realtimer: true,
              start: 0,
              end: dataZoomEnd,
            },
            {
              type: 'inside',
              bottom: '40px',
              realtimer: true,
              start: 0,
              end: dataZoomEnd,
            },
          ],
          series: option.series,
        }
        return myOption
      },
      keyUpper(obj) {
        for (let key in obj) {
          let Allkey = key.toUpperCase()
          if (Allkey === key) {
            obj[Allkey] = obj[key]
          } else {
            obj[Allkey] = obj[key]
            delete obj[key]
          }
        }
        return obj
      },
      searchData() {
        if (this.showMobileSearch && this.$refs.table) {
          this.$refs.table.doQuickSearch()
        }
      },
      getCountFieldName() {
        if (!this.countQuery.countField) return ''
        let countFieldObj = this.countFields.find(
          (e) => e.field === this.countQuery.countField
        )
        return countFieldObj && countFieldObj.comment
          ? countFieldObj.comment
          : ''
      },
      closeAllPopup() {
        if (this.$refs['dropdown-item-more']) {
          this.$refs['dropdown-item-more'].toggle(false)
        }
        this.show = false
      },
      resetOrder() {
        this.countQueryFileds.forEach((item) => {
          this.countQuery[item.field] = ''
        })
      },
      hideSearch() {
        this.orderSelect = false
        let dataList = this.countQueryFileds
        let dataItem = []
        dataList.forEach((item) => {
          if (this.countQuery[item.field]) {
            dataItem.push(this.countQuery[item.field])
          }
        })
        if (dataItem.length > 0) {
          this.orderSelect = true
        }
        this.initClick()
        this.closeAllPopup()
      },
      resetCountOrder() {
        this.countQuery.countType = ''
        this.countQuery.calcField = ''
      },
      hideCountSearch() {
        this.orderTypeSelect = false
        if (
          this.countQuery.countType !== '' ||
          this.countQuery.calcField !== ''
        ) {
          this.orderTypeSelect = true
        }
        this.initClick()
        this.showType = false
      },
      changeCountField() {
        this.dropdownActive = false
        this.showType = true
      },
      changeMore() {
        this.dropdownActive = false
        this.show = true
      },
      resetListStatus() {
        this.listLoading = false
        this.listFinished = false
        this.listError = false
        this.pageResult.page = 1
        this.data = []
      },
      calcListHeight() {
        const that = this
        if (!that.isMobile) return

        this.$nextTick(() => {
          const screenHeight =
            document.documentElement.clientHeight || window.innerHeight
          let topHeight = 0
          if (that.$refs.tabBar)
            topHeight += that.$refs.tabBar.offsetHeight || 0
          const filterDom = document.querySelector('.dropDown')
          if (filterDom) topHeight += filterDom.offsetHeight || 0
          const searchDom = document.querySelector('.mobile-quick-search')
          if (searchDom) topHeight += searchDom.offsetHeight || 0
          // 核心：content-container 高度 = 屏幕高度 - 顶部高度 - 少量边距
          const contentContainer = document.querySelector('.content-container')
          if (contentContainer && that.isMoreUrl) {
            contentContainer.style.height = `${
              screenHeight - topHeight - 10 - 56
            }px`
          }

          const listContainer = document.querySelectorAll('.mobile-chart-list')
          if (listContainer && listContainer.length) {
            listContainer.forEach((item) => {
              item.style.height = '100%'
              item.style.overflowY = 'auto'
            })
          }
        })
      },
      async onListLoad() {
        if (!this.isMobile || !this.mobileNeedPagination) return
        if (this.listLoading) return
        this.listError = false
        try {
          const param = {
            pageBean: {
              page: this.pageResult.page,
              pageSize: this.pageResult.pageSize,
            },
            querys: [],
          }
          await this.loadData(param, () => {
            this.listLoading = false
            if (
              this.pageResult.total &&
              this.data.length >= this.pageResult.total
            ) {
              this.listFinished = true
            } else {
              this.pageResult.page += 1
            }
          })
        } catch (err) {
          this.listLoading = false
          this.listError = true
          console.error('列表加载失败：', err)
        }
      },
      handleMobileSearch() {
        if (!this.showMobileSearch) return

        clearTimeout(this.quickSearchInput)
        this.quickSearchInput = setTimeout(() => {
          this.resetListStatus()
          const defaultParams = {
            pageBean: this.pageResult,
            querys: [],
          }
          this.loadData(defaultParams)
        }, 300)
      },
      toggleRowExpand(index) {
        this.$set(this.expandedRows, index, !this.expandedRows[index])
      },
    },
  }
</script>
<style scoped lang="scss">
  $primary-color: #409eff;
  $gray-color: #8c8c8c;
  $light-gray: #f4f4f4;
  $border-color: #ebeef5;
  $mobile-tab-height: 32px;
  $mobile-search-height: 36px;
  $full-screen-z-index: 9999;

  ::v-deep .el-table--scrollable-y {
    max-height: 100%;
    height: 100%;
  }

  .mobile-chart-tabs {
    ::v-deep .van-tab--active {
      color: $primary-color;
      background-color: #fff;
      border-radius: 40px;
    }

    ::v-deep .van-tabs__nav--card {
      border: 2px solid $light-gray;
      border-radius: 32px;
      height: $mobile-tab-height;
      margin: 0;
      align-items: center;
    }

    ::v-deep .van-tabs__nav--card .van-tab {
      border: none;
      padding: 0;
      height: 30px !important;
      line-height: 30px !important;
    }

    ::v-deep .van-tabs__wrap {
      width: 82px;
      height: $mobile-tab-height;
    }

    .icon-tubiaoshitu,
    .icon-shujushitu1 {
      font-size: 22px;
      line-height: $mobile-tab-height;
    }
  }

  .mobile-chart-tabs-url {
    ::v-deep .van-tabs__nav--line {
      .van-tab--active {
        color: $primary-color;
        background-color: #fff;
      }

      .van-tabs__line {
        background-color: $primary-color;
      }

      .vertical-chart {
        height: 400px;
      }
    }
  }

  .ht-chart {
    position: relative;
    overflow: hidden;
    .full-screen-icon {
      position: absolute;
      font-size: 22px;
      color: $gray-color;
    }
    .mobile-quick-search {
      padding: 8px 12px;
      box-sizing: border-box;
      width: 100%;

      ::v-deep .van-search {
        --van-search-background-color: #f7f8fa;
        --van-search-input-height: $mobile-search-height;
        --van-search-border-radius: 18px;
      }
    }
    ::v-deep .el-tabs--card > .el-tabs__header {
      border: none;
      margin: 0;
    }

    ::v-deep .el-tabs__content {
      padding: 20px 0;
    }

    ::v-deep .el-tabs__nav {
      border-radius: 4px;
    }

    ::v-deep .el-tabs__item {
      height: 35px;
      line-height: 35px;
    }
    .toggle-charts-button {
      ::v-deep .el-tabs__item {
        background-color: #e6e6e6;
        height: 36px;
        line-height: 36px;
        padding: 0 15px !important;
        font-weight: bold;
      }

      ::v-deep .el-tabs__item.is-active {
        background-color: #fff;
        border-bottom: 1px solid #e6e6e6;
      }
    }
    ::v-deep .common-tools {
      min-height: 34px;

      .refresh-btn-icon {
        position: absolute;
        top: 0;
        right: 0;
      }
    }
  }

  .horizontal-echarts-filters {
    display: block;
    width: 100%;
    box-sizing: border-box;
    padding: 0 8px;
  }
  .dropDown {
    width: 100%;
    padding: 4px 0;
    box-sizing: border-box;
    z-index: 999;
    position: relative;
  }
  ::v-deep .dropdown-z-index {
    z-index: $full-screen-z-index;

    .van-dropdown-menu__bar,
    .van-dropdown-item {
      z-index: $full-screen-z-index;
    }
  }
  ::v-deep .full-screen-chart {
    width: 100vw;
    height: 100vh;
    overflow: scroll;
    margin: 0 auto;
    padding-bottom: 16px;

    .refresh-btn-icon {
      right: 20px;
      font-size: 22px;
      color: $gray-color;
    }

    .van-search {
      width: 100%;
      padding: 8px 0;
    }
    .pagination-panel {
      .el-pagination {
        .el-pagination__sizes {
          .el-input__inner {
            height: 32px;
          }
        }
      }
    }
    .list-item-col {
      width: 100%;
    }
    .mobile-chart-fixed-height {
      height: 350px;
    }
    .mobile-list-auto-height {
      flex: 1;
      height: calc(100vh - 350px - 20px);
    }
  }
  .flex-class {
    display: flex;
    align-items: center;
    height: 54px;
    padding-left: 16px;
  }
  .content-container {
    height: calc(100vh - 60px);
    display: flex;
    flex-direction: column;
    flex-shrink: 0;
    overflow: hidden;
    .ht-statistical-chart-container & {
      height: 100%;
    }
  }
  .mobile-chart-fixed-height {
    height: 300px;
    flex-shrink: 0;
    width: 100%;
    overflow: hidden;
  }
  .mobile-list-auto-height {
    flex: 1;
    width: 100%;
    overflow: hidden;
  }

  ::v-deep .mobile-chart-list {
    width: 100%;
    height: 100%;
    overflow-y: auto;
    box-sizing: border-box;
    padding: 0;
  }
  .chart-name {
    font-size: 14px;
  }

  ::v-deep {
    .fullscreen-popup {
      width: 100vw;
      height: 80vh;
      left: 0;
      top: auto;
      bottom: 0;
      right: auto;
      transition: none;
      z-index: $full-screen-z-index;
    }

    .fullscreen-popup-active {
      height: calc(80vh - 150px);
    }

    .fullscreen-input {
      width: 100%;
      text-align: right;
      writing-mode: horizontal-tb;
      direction: ltr;
    }

    // 下拉菜单文本
    .more-dropdown-span {
      width: 100%;
      display: inline-block;
      word-break: break-all;
      word-wrap: break-word;
      text-align: left;
    }

    .more-dropdown-item::after {
      display: none;
    }

    // 单选/复选框布局
    .more-dropdown-select,
    .query-params-dropdown-item {
      .van-radio-group--horizontal,
      .van-checkbox-group--horizontal {
        justify-content: flex-start;

        .van-radio,
        .van-checkbox {
          margin: 5px 24px 0 5px;
        }
      }
    }

    // 更多按钮
    .more_btn {
      display: flex;
      align-items: center;

      .more_btn_icon {
        margin-left: 5px;
        color: #dcdee0;
      }
    }

    // 下拉菜单导航栏
    .van-dropdown-menu__bar {
      z-index: 2;
      box-shadow: none;
      justify-content: space-around;

      & > div {
        flex: 0 1 auto;
      }
    }

    // 自定义弹窗下拉菜单
    .customMobileDialog-vue {
      .van-dropdown-menu__bar {
        & > div:nth-child(n + 3) {
          .van-dropdown-menu__title::after {
            display: none;
          }
        }
      }
    }

    .customMobileDialog-vue-has {
      .van-dropdown-menu__bar {
        & > div:nth-child(n + 4) {
          .van-dropdown-menu__title::after {
            display: none;
          }
        }
      }
    }

    // 下拉菜单箭头
    .van-dropdown-menu__title::after {
      top: 42%;
      font-size: 10px;
      border: 4px solid;
      right: -7px;
      border-color: transparent transparent $gray-color $gray-color;
    }

    .van-dropdown-menu__title--active {
      color: $primary-color;
    }

    // 排序/计数字段样式
    .orderSelect,
    .countField {
      color: $primary-color;

      svg,
      .text {
        color: $primary-color;
      }
    }

    .moreSelect.van-dropdown-menu__title,
    .orderSelect.van-dropdown-menu__title {
      &::after {
        display: none;
      }
    }

    .van-dropdown-menu__title--active::after {
      border-color: transparent transparent $primary-color $primary-color;
    }

    // 日期输入框
    .query-params-dropdown-item {
      .mobile-input_date {
        height: 44px;
      }
    }

    // 输入框清除按钮
    .el-input__clear {
      height: 44px;
      margin-top: 8px;
    }

    .van-dropdown-menu__title {
      color: $gray-color;
    }

    .countField {
      .van-ellipsis {
        color: $primary-color;
      }

      &::after {
        border-color: transparent transparent $primary-color $primary-color;
      }
    }

    // 列表项样式
    .list-item-row {
      padding: 12px 16px;
      border-bottom: 10px solid #f5f5f5;
      margin-bottom: 8px;
      position: relative;
      display: flex;
      flex-direction: column;
      box-sizing: border-box;
    }

    .list-item-content {
      display: flex;
      flex-wrap: wrap;
      gap: 8px;
      font-size: 14px;
      color: #333;
      max-height: 90px;
      overflow: hidden;
      transition: max-height 0.4s ease-in-out;
      width: 100%;
      line-height: 24px;
    }

    .list-item-content-expanded {
      max-height: 2000px;
      overflow: visible;
    }

    .list-item-col {
      width: 100%;
      box-sizing: border-box;
      word-break: break-all;
      line-height: 22px;
    }

    .list-item-col-value {
      color: #333;
      font-size: 14px;
    }

    .list-item-col-label {
      color: $gray-color;
      font-size: 14px;
    }

    // 展开/收起按钮
    .toggle-expand-btn {
      align-self: center;
      display: flex;
      align-items: center;
      gap: 4px;
      font-size: 13px;
      color: $primary-color;
      cursor: pointer;
      padding: 6px;

      i {
        font-size: 14px;
      }

      &:active {
        background: #e6f0ff;
      }
    }

    // 空状态/加载状态
    .van-empty {
      margin: 30px auto;
    }

    .van-list__loading,
    .van-list__finished,
    .van-list__error {
      padding: 15px 0;
      color: #999;
      font-size: 12px;
    }
  }
</style>

<style>
  .table-responsive table {
    cellspacing: 0;
    *border-collapse: collapse;
    border-spacing: 0;
    width: 100%;
    border-left: 0.5px solid #ebeef5;
    border-top: 0.5px solid #ebeef5;
    font-size: 14px;
    color: #666;
  }

  .table-responsive .bordered tr:hover {
    background: #f5f7fa;
    -o-transition: all 0.1s ease-in-out;
    -webkit-transition: all 0.1s ease-in-out;
    -moz-transition: all 0.1s ease-in-out;
    -ms-transition: all 0.1s ease-in-out;
    transition: all 0.1s ease-in-out;
  }

  .table-responsive .bordered th {
    height: 35px;
    background: #f4f4f4;
    border-right: 1px solid #ebeef5;
    text-align: center;
    cellspacing: 0;
  }

  .table-responsive .bordered td {
    padding: 7px;
    border-right: 1px solid #ebeef5;
    border-bottom: 1px solid #ebeef5;
    text-align: center;
    cellspacing: 0;
  }

  .table-responsive .bordered tbody tr:hover {
    background: #f5f7fa;
    -o-transition: all 0.1s ease-in-out;
    -webkit-transition: all 0.1s ease-in-out;
    -moz-transition: all 0.1s ease-in-out;
    -ms-transition: all 0.1s ease-in-out;
    transition: all 0.1s ease-in-out;
  }
  .search-style {
    margin-bottom: 10px;
  }
  .search-style .el-button {
    margin-left: 28px;
  }
  .mobile-van-action-chart .van-field__control {
    text-align: right;
  }
  .preview-chart .line {
    border-bottom: 12px solid #f6f6f6;
  }
  .table-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 10px;
  }
</style>
