StoreRank.nvue 9.97 KB
<template>
  <view style="margin-bottom: 20rpx;">
    <CardNvue :title="t(cardProps.title)" style="padding-bottom: 0;" :auto-height="true">
      <template #filter>
        <view class="filter filter-border noBg" @tap="handleTapToChoose" v-if="!hide">
          <uv-text :text="selectText || t('maintenance.common.select')" align="center" size="26rpx" color="#90949D" :lines="1"></uv-text>
          <uv-icon name="arrow-right" color="#90949D" size="12" />
        </view>
      </template>
      <template #content>
        <StoreRankListNvue :data="sourceData" :indicatorKey="xAxisFields[checkIndex || 0]?.key||''"></StoreRankListNvue>
      </template>
    </CardNvue>
    <uv-popup ref="typePopRef" round="24rpx" @maskClick="handleMaskClick">
      <view class="pop_header">
        <view style="width: 22px;" />
        <text class="pop_header_text">{{ t('app.title.kpiSelect') }}</text>
        <uv-icon name="close" size="22" class="pop_header_icon" @tap="handleCloseTypeSelect"></uv-icon>
      </view>
      <scroll-view scroll-y="true" style="height: 240px;">
        <view class="l_type">
          <view class="l_type_item" v-for="(item,index) in xAxisFields" :key="item.key"
            :class="{'l_type_item_active':index === checkIndex}" @tap="handleChangeCheckType(index)">
            <text class="l_type_item_text"
              :class="{'l_type_item_active_text':index === checkIndex}">{{ item.name }}</text>
          </view>
        </view>
      </scroll-view>
    </uv-popup>
  </view>
</template>
<script setup>
  import {
    getStoreRankApi
  } from '@/api'
  import CardNvue from './Card.nvue'
  import ChartsNvue from '@/components/Charts.nvue'
  import StoreRankListNvue from '../../../components/StoreRankList.nvue'
  import {
    computed,
    onMounted,
    ref,
    Fragment,
    nextTick
  } from 'vue'
  import {
    getStageObj,
    rpx2Px,
    formatIndicatorValue
  } from '@/utils'
  import { t } from '@/plugins/index.js'


  const props = defineProps({
    cardProps: {
      type: Object,
      default: () => ({
        title: 'PreMenu.regionalRanking', // '门店排行',
        titleIcon: true,
        pageSize: 5,
        height: '506px'
      })
    }
  })

  const rankingRef = ref(null)
  const options = ref({})

  const initData = async (params) => {
    options.value = params
    renderChartData()
  }

  // 所有能排序的数据
  const xAxisFields = ref([])
  // 当前选中的数据
  const selectIndex = ref(0)

  const selectText = computed(() => {
    return xAxisFields.value[selectIndex.value]?.name || ''
  })

  const hide = ref(false)
  const hideFilter = () => {
    hide.value = true
  }

  // 渲染数据
  const sourceData = ref([])
  // 渲染图表
  const renderChartData = async () => {
    try {
      const account = await getStageObj('account')
      const params = {
        page: 1,
        pageSize: props.cardProps.pageSize || 5,
        startDate: options.value.startDate,
        sortKey: checkIndex.value ? xAxisFields.value[checkIndex.value].key : '',
        sortType: 'desc',
        endDate: options.value.endDate,
        groupId: options.value.groupId ? options.value.groupId : -1,
        orgIds: account.id,
        chartIds: 100,
        accountIds: account.id
      }
      if (!params.sortKey) {
        delete params.sortKey
      }

      const {
        data
      } = await getStoreRankApi(params)
      const {
        mall_ranking
      } = data
      // 设置源数据
      let keyIndex = 0
      let flag = false
      // 根据maxVal设置排行百分比值
      xAxisFields.value = mall_ranking.xaxis.data
        .map((item, index) => {
          const parseItem = JSON.parse(item)
          // 获取第一个排行数据的索引
          if (keyIndex === 0 && parseItem.key && !flag) {
            keyIndex = index
            flag = true
          }

          return {
            ...parseItem,
            originalIndex: index
          }
        })
        .filter(item => item.key)

      sourceData.value = getSortedSeriesData(mall_ranking.series || [], keyIndex)

    } catch (e) {
      console.log(e);
    }
  }

  // 获取排序后的源数据
  const getSortedSeriesData = (list, keyIndex) => {
    return (list
      ?.map(item => {
        const rawValue = item.data[selectIndex.value + keyIndex]
        let value = 0

        // 处理特殊值(根据你的数据类型可能需要扩展)
        if (typeof rawValue === 'number') {
          value = rawValue
        } else if (!isNaN(parseFloat(rawValue))) {
          value = parseFloat(rawValue)
        }

        return {
          name: item.data[1], // 获取门店名称
          value: value
        }
      }) || []).sort((a, b) => {
      return b.value - a.value
    })
  }

  /**************************** pop选择相关 *********************************/
  const typePopRef = ref(null)
  const checkIndex = ref('')
  const handleTapToChoose = () => {
    uni.hideTabBar()
    checkIndex.value = selectIndex.value || 0
    typePopRef.value?.open('bottom')
  }
  const handleMaskClick = () => {
    uni.showTabBar()
  }
  const handleCloseTypeSelect = () => {
    handleMaskClick()
    typePopRef.value?.close()
  }
  const handleChangeCheckType = (index) => {
    checkIndex.value = index
    handleConfirmSelectType()
  }
  const handleConfirmSelectType = () => {
    handleMaskClick()
    selectIndex.value = checkIndex.value
    typePopRef.value?.close()
    renderChartData()
    // renderRankingCharts(getSortedSeriesData())
  }



  /**************************** 图表渲染相关 *********************************/
  const renderRankingCharts = (val) => {
    const option = rightCenterBarConfig(val, '', 15)
    rankingRef.value?.initCharts(option)
  }

  function rightCenterBarConfig(val) {
    const xData = val.map(item => item.name)
    const yData = val.map(item => item.value)

    const yDataCl = JSON.parse(JSON.stringify(yData)).sort((a, b) => (b - a))
    const clLength = yDataCl.length
    return {
      tooltip: {
        show: true,
        trigger: "axis",
        textStyle: {},
        confine: true,
        triggerOn: 'click'
      },
      grid: {
        bottom: 0,
        left: 0,
        right: 0,
        top: 16
      },
      xAxis: {
        type: "value",
        splitLine: {
          show: false,
        },
        axisTick: {
          show: false
        },
        axisLine: {
          show: false
        },
        axisLabel: {
          show: false,
        }
      },
      yAxis: [{
        type: "category",
        data: xData,
        axisTick: {
          show: false
        },
        axisPointer: {
          type: "shadow",
        },
        axisLine: {
          show: false,
          lineStyle: {
            color: "#BDD8FB",
            fontSize: 12
          }
        },
        axisLabel: {
          fontSize: 12,
          textStyle: {
            color: "#202328",
            align: "left",
            verticalAlign: 'top',
            padding: [-30, 0, 0, 10],
            // padding: [rpx2Px(-64), 0, 0, rpx2Px(20)],
            fontFamily: 'DingTalk_JinBuTi'
          },
          // #ifdef APP-NVUE
          formatter: `function(value, index) {
						const lIndex = ${clLength}-index
						let rankDisplay = ''
						if(lIndex === 1){
							rankDisplay = "{icon1|}"
						}else if(lIndex === 2){
							rankDisplay = "{icon2|}"
						}else if(lIndex === 3){
							rankDisplay = "{icon3|}"
						}else {
							rankDisplay = '{indexs|' + lIndex + '}'
						}
            let displayValue = value
            if(value.length > 20){
              displayValue = value.slice(0,20) + '...'
            }
						return \`\${rankDisplay} \${displayValue}\`;
					}`,
          // #endif
          // #ifndef APP-NVUE
          formatter: function(value, index) {
            const lIndex = clLength - index
            let rankDisplay = ""
            if (lIndex === 1) {
              rankDisplay = "{icon1|}"
            } else if (lIndex === 2) {
              rankDisplay = "{icon2|}"
            } else if (lIndex === 3) {
              rankDisplay = "{icon3|}"
            } else {
              rankDisplay = `{indexs|${lIndex}}`
            }
            let displayValue = value
            if (value.length > 20) {
              displayValue = value.slice(0, 20) + '...'
            }
            return `${rankDisplay} ${displayValue}`
          },
          // #endif
          rich: {
            icon1: {
              height: 16,
              align: 'left',
              backgroundColor: {
                image: rank1
              }
            },
            icon2: {
              height: 16,
              align: 'left',
              backgroundColor: {
                image: rank2
              }
            },
            icon3: {
              height: 16,
              align: 'left',
              backgroundColor: {
                image: rank3
              }
            },
            indexs: {
              width: 16,
              align: 'center',
              fontSize: 12,
            }
          }
        }
      }, {
        type: 'category',
        inverse: true,
        axisTick: 'none',
        axisLine: 'none',
        show: true,
        offset: -7,
        axisLabel: {
          textStyle: {
            color: '#202328',
            fontSize: 13,
            align: 'right'
          },
          verticalAlign: 'bottom',
          padding: [20, 2, 18, 0],
        },
        data: yDataCl,
      }],
      series: [{
        type: "bar",
        stack: "all",
        data: yData,
        barWidth: 14,
        itemStyle: {
          color: "#4277F7",
          borderRadius: [3, 3, 3, 3],
        },
        showBackground: true,
        backgroundStyle: {
          color: '#F7F8FB',
          borderRadius: [3, 3, 3, 3],
        }
      }]
    };
  };

  defineExpose({
    initData,
    hideFilter
  })
</script>
<style lang="scss" scoped>
  @import '@/styles/normal.scss';
  ::v-deep(.uv-safe-area-inset-bottom){
    padding-bottom: 0!important;
    }
    .filter-border{
      max-width: 200rpx;
    }
    .noBg{
      background-color: transparent !important;
    }
</style>