bizCharts 中遇到的问题

需求:写一个统计图如下

要求:点击图例,对应的数据可以消失,Y轴是数据中的日期,X轴是一天24小时的时间段,数据为7天/30天/90天,要求统计图可以滚动,X轴不会跟着滚走。

分析:

  1. 统计图可以滚动,那么就是 Chart 高度不固定,是动态的,我将7天数据高度算好以后求出来的 单个高度✖️数据 ,在外围整了个7天的高度限制,然后超出数据高度就开始滚动
    <div style={{ overflow: 'auto', height: 190 }}><Chart height={23 * datas.length}>.....<Chart></div>
  2. X轴需要手写定位在这个位置,我是直接按照X轴的位置写的,把样式写好以后,将原有的毙掉<Axis name="timeRange" visible={false} />
  3. 点击对应图例消失,这就是直接将图例的这个类型的visible变成false,然后将总数据里把这个所有类型里visible是true的筛出来。
    <Chart
    
    height={23 * datas.length}
    
    padding={['auto', 'auto']}
    
    data={datas}>// 就是这个东西
  4. X轴一天24小时,还要和后端数据返回的时间段对应,那么可以把x轴的时间按照一天的分钟数去计算。

    
     <Chart
              height={23 * datas.length}
              padding={['auto', 'auto']}
              data={datas}
              forceFit
              scale={{
                timeRange: {
                  max: 1440,// 24:00的分钟数是最大值
                  tickInterval: 120,// 间隔两小时展示一次
                  min: 0,// 00:00的分钟数是最小值
                },
              }}
            >
  5. 数据的处理,先需要知道自己需要什么样子的数据。那么现在知道的就是,hover需要的值,x、y轴的值都需要,以及每条数据是什么图例的类型,这几个是必须值,是需要将所有数据处理为需要的字段的数组

后端返回的数据结构是:

 data:{
  date: string; //日期,形如:2024-01-01
  useData: {
    duration: number; // 使用时长,单位:min
    startTime: string; // 开始时间,形如:2024-01-01 12:00:00
    endTime: string; // 结束时间,形如:2024-01-01 13:00:00
   
  }[];
}[];

分析:

可以看到 data中的所有数据结构,都是按天(date)返回的,一天中又有多个数据( useData[ ] ),我们分析了所有数据要一一展示,那么我们就需要循环useData,然后将需要的值push到一个新数组,期间也给我们也要通过计算将每个数据的类型也放到新的数据组中。

 data.push({
        timeString,// hover 里面需要展示的时间段
        date: G.moment(item.date).format('MM-DD'),//y轴 日期
        status,// 图例状态类型
        percent: percent,// hover 里面需要展示的时间段
        timeRange: [startMins, endMins],// x轴对应的数据段,[0,20] 表示[00:00-00:20],这个再配合我们对scale的定义就可以让我们的时间段展示在统计图了
      });

// mins的计算方法
// 计算分钟数
export const calculateMinutes = (value) => {
  const time = moment(value, 'HH:mm');// 这里根据需求写,我这里是算当天当前时间是多少分钟,所以我的value值就是'12:00'这种格式
  // 计算从当天开始到 time 的毫秒数
  const milliseconds = time.diff(moment().startOf('day'));
  // 将毫秒数转换为分钟
  const minutes = milliseconds / 60000;
  return minutes;
};

处理完以后的数据结构应该是{xx:xx,xx:xx,...}[]

 按照需求将24小时的时间段获取到,写自定义X轴用

// 获取 00:00-24:00的时间段数组['00:00','02:00',....,'24:00']
export const generateHourlyArrayWithMoment = () => {
  const hourlyArray = [];
  let hour = moment().startOf('day').hour(0);

  for (let i = 0; i <= 12; i++) {
    const formattedHour = hour.format('HH:mm');
    hourlyArray.push(formattedHour);
    hour.add(2, 'hours');
  }
  hourlyArray[hourlyArray.length - 1] = '24:00';
  return hourlyArray;
};

// x 轴的展示,写法提示
    <div className={styles.xAxis}>
        {xAxis.map((item, index) => {
          const interval = 608 / 11; // 根据原X轴计算的间隔 
          return (
            <div className={styles.xAxisline} style={{ left: interval * (index + 1) }}>
              <span className={styles.xAxisNumber}>{item}</span>
            </div>
          );
        })}
      </div>

styles={ // 具体样子自己调整,我这个是不完整的样式
.xAxis {
  position: relative;
  left: -14px;
}

.xAxisline {
  position: absolute;
  display: inline-block;
}

.xAxisNumber {
  position: relative;
}}

写的时候遇到一个问题就是同一天的hover只显示第一个的真实数据,经过好几个小时尝试,发小只要在gemo中加tooltip参数即可让让每天都多条数据hover正常显示对应数据

 <Geom
            type="interval"
            position="date*timeRange"
            color={['status', (item) => colorList[item]?.color || '#fff']} // 我的颜色是根据我处理好的每条数据对应的图例类型进行书写的,当没有一条数据的时候会报错,必须给他一个'#fff'
            tooltip={[
              'timeString*percent',
              (timeString, percent) => {
                return {
                  value: percent,
                  name: timeString,
                  title: `${timeString}:${percent}%`,
                };
              },
            ]}// 靠这个救活了自定义的Tooltip (这个好像我之前写过在前边博客,这里就不重复写了)
          />

-------------------------------分割线-------------------------------------------------------------------

领导让手写这个功能了,下边附上代码

return (
    <>
      <div
        style={{
          display: 'flex',
          justifyContent: 'center',
          alignItems: 'center',
          fontSize: '12px',
          margin: '10px 10px 5px 10px',
          flexWrap: 'wrap',
        }}
      >
        {minTypes.map((item) => {
          const color = minTypes.includes(item) ? colorList[item].color : 'rgba(25, 34, 59, 0.2)';
          const bgColor = geomList.filter((geom) => geom.text === item)[0].visible ? colorList[item].color : '#C7C8CA';
          const checked = geomList.filter((geom) => geom.text === item)[0].visible;
          const disabled = !minTypesData.filter((i) => i.text === item)[0].visible;

          return (
            <div
              style={{
                border: `2px solid ${bgColor}`,
                backgroundColor: checked ? `${color}0b` : undefined,
                cursor: disabled ? 'not-allowed' : 'pointer',
              }}
              className={styles.legendBox}
              onClick={disabled ? undefined : toggleGeom}
              data-type={item}
              key={item}
            >
              <div className={styles.legendContain} data-type={item}>
                <div
                  data-type={item}
                  style={{
                    width: '10px',
                    height: '10px',
                    backgroundColor: bgColor,
                    marginRight: '10px',
                  }}
                />
                <div data-type={item}>{colorList[item].name}</div>
              </div>
            </div>
          );
        })}
      </div>

      <div style={{ overflow: 'auto', height: 190 }}>
        {datas.map((item, key) => (
          <div className={styles.yAxis} key={key}>
            <div className={styles.yAxisText}>{item.date}</div>
            <div className={styles.yAxisline}>
              {item.useData.map((useItem, index) => {
                if (!useItem) return <></>;
                const content = (
                  <div>
                    <div className={styles.hoverLine}>
                      <span className={styles.hoverLeft}>
                        {formatMessage({ id: 'space.phoneBoothAnalysis.usage-time' })}:
                      </span>
                      <span>{useItem?.timeString}</span>
                    </div>

                    <div className={styles.hoverLine}>
                      <span className={styles.hoverLeft}>
                        {formatMessage({ id: 'space.phoneBoothAnalysis.duration-proportion' })}:
                      </span>
                      <span>{useItem.percent}%</span>
                    </div>
                  </div>
                );
                const widthPercent = ((useItem.endMins - useItem.startMins) / DAY_MINUTES) * 100;
                const startPercent = (useItem.startMins / DAY_MINUTES) * 100;

                return (
                  <Popover content={content} trigger="hover" key={`${index} + ${useItem.startMins}`}>
                    <span
                      className={styles['time-box']}
                      style={{
                        background: colorList[useItem.minType]?.color || 'transparent',
                        width: `${widthPercent}%`,
                        left: `${startPercent}%`,
                      }}
                    ></span>
                  </Popover>
                );
              })}
            </div>
          </div>
        ))}
      </div>
      <div className={styles.xAxis}>
        {xAxis.map((item, index) => {
          const interval = 663 / 12; // 根据原X轴计算的间隔
          return (
            <div className={styles.xAxisline} style={{ left: interval * (index + 1) }} key={index}>
              <span className={styles.xAxisNumber}>{item}</span>
            </div>
          );
        })}
      </div>
    </>
  );
};

style{
.yAxis {
  color: #9aa9b5;
  font-size: 12px;
  height: 26px;
  line-height: 26px;
  display: flex;
  align-items: center;
}

.yAxisText {
  margin-right: 20px;
  width: 38px;
}

.yAxisline {
  height: 1px;
  width: 663px;
  background: #f4eded;
  position: relative;
}

.time-box {
  height: 6px;
  border-radius: 5px;
  position: absolute;
  top: -3px;
}

.hoverLine {
  width: 100%;
  font-weight: 400;
  display: flex;
  justify-content: space-between;
  font-size: 14px;
  line-height: 30px;
  color: #19223b;
  margin-bottom: 0;
}

.hoverLeft {
  margin-right: 12px;
  display: inline-block;
  min-width: 120px;
}}

截止现在,这个需求就写完了,有什么问题可以留言哦~

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值