Echarts-丝带图

Echarts-丝带图

demo地址

打开CodePen

什么是丝带图?

丝带图是Power BI中独有额可视化视觉对象,它的工具提示能展示指标当期与下期的数据以及排名。需求:使用丝带图展示"2022年点播订单表"不同月份不同点播套餐对应订单数据。

效果

在这里插入图片描述

思路

由于丝带图是Power BI中独有额可视化视觉对象,所以目前没得任何示例参考,所以只能自己构思使用echarts还原了。当然还有完善的余地,中间的连线不够平滑,可根据产品需求采用某种曲线函数去生成一组点位。

1. 以散点图画出柱状堆叠效果(柱状图的堆叠图无法满足hover小块效果)
  - y轴分成100个刻度,每个刻度代表1%,以控制大数据视图效果
2. 在柱状图两根柱之间构建6个点,使用面积图,连接2块柱
  - 柱中间点位取的是y轴的平均值
  - (若想构建的曲线细腻,可以使用曲线函数来构建这部分的点)
3. 再使用上面6个点中的下面点绘制透明区域

核心代码

  • 以散点图构建柱状图
function createOption(initData) {
  const initDataResult = createData(initData);
  const { list, legendData, xAxisData, seriesDataMap, max } = initDataResult;
  const seriesData = [];

  for (const seriesIndex in Object.keys(seriesDataMap)) {
    const name = Object.keys(seriesDataMap)[seriesIndex];
    const data = seriesDataMap[name];
    seriesData.push({
      name,
      type: 'scatter',
      symbol: 'rect',
      z: 3,
      itemStyle: {
        opacity: 1
      },
      label: {
        show: true,
        color: '#fff',
        formatter: (params) => formatMoney(params.data.realValue, 0)
      },
      tooltip: {
        trigger: 'item',
        formatter: (params) => {
          return `<div>
          <div>年度月份:${params.name}</div>
          <div>${params.seriesName}${formatMoney(params.data.realValue, 0)}</div>
        </div>`;
        }
      },
      data: getChartData({ data, name })
    });
  }

  function getChartData({ data = [], name }) {
    const dataResult = [];
    data?.forEach((value, dateIndex) => {
      const y = maxY * (value / max);
      const ySize = maxHeight * (y / maxY);
      const offset = getOffset({ list, dateIndex, name, max });
      const radioValue = y + offset > 100 ? 100 : y + offset;

      dataResult.push({
        name,
        value: radioValue,
        radioValue,
        realValue: value,
        symbolOffset: [0, '50%'],
        symbolSize: [50, ySize]
      });

      if (dateIndex < data?.length - 1) {
        new Array(3).fill(0).forEach((_, lineIndex) => {
          dataResult.push({
            value: '',
            radioValue,
            realValue: value,
            isLine: true,
            lineIndex
          });
        });
      }
    });
    return dataResult;
  }

  const lineSeries = createLineChart({ seriesData, initDataResult });

  return {
    option: {
      legend: {
        data: legendData
      },
      xAxis: {
        data: xAxisData,
        axisTick: {
          show: false
        }
      },
      series: [...seriesData, ...lineSeries]
    }
  };
}
  • 生成折线图数据
function getLineData(data, name, isSpace = false) {
  const result = data?.map((_, index) => {
    const dateIndex = Math.floor(index / 4);
    const lineIndex = index % 4;

    const item = data?.[index] || {};
    const lastItem = data?.[index - (4 - lineIndex)] || {};
    const nextItem = data?.[index + (4 - lineIndex)] || {};

    const offset = getOffset({ list, dateIndex, name, max });
    const nextOffset = getOffset({ list, dateIndex: dateIndex + 1, name, max });
    let spaceValue;
    let value = item.radioValue - offset;

    switch (lineIndex) {
      case 0:
        spaceValue = offset;
        break;
      case 1:
        spaceValue = offset;
        if (!nextItem?.radioValue) {
          value = undefined;
        }
        break;
      case 2:
        spaceValue = (nextOffset + offset) / 2;
        value = (nextItem.radioValue + item.radioValue) / 2 - spaceValue;
        break;
      case 3:
        spaceValue = nextOffset;
        value = nextItem.radioValue - nextOffset;
        if (!lastItem?.radioValue) {
          value = undefined;
        }
        break;
    }
    if (!lastItem?.radioValue && !nextItem?.radioValue) {
      value = undefined;
    }
    // console.log(lineIndex, item, offset, nextOffset, spaceValue, value);
    const newItem = {
      ...item,
      value: isSpace ? spaceValue : value
    };
    return newItem;
  });
  // console.log('result', result);
  return result;
}
  • 生成折线图配置
function createLineChart({ seriesData = [], initDataResult }) {
  const { list, max } = initDataResult;
  const spaceLineSeries = [];
  const lineSeries = [];
  // console.log('seriesData', seriesData);
  for (const seriesIndex in seriesData) {
    const seriesItem = seriesData[seriesIndex];
    const defaultLineSeries = {
      type: 'line',
      name: seriesItem.name,
      stack: `Line-${seriesIndex}`,
      smooth: 0.3,
      lineStyle: {
        width: 0,
        opacity: 0
      },
      symbol: 'none',
      showSymbol: false,
      triggerLineEvent: true,
      silent: true,
      areaStyle: {},
      emphasis: {
        focus: 'series'
      }
    };

    spaceLineSeries.push({
      ...defaultLineSeries,
      areaStyle: {
        opacity: 0
      },
      data: getLineData(seriesItem?.data, seriesItem.name, true)
    });

    lineSeries.push({
      ...defaultLineSeries,
      data: getLineData(seriesItem?.data, seriesItem.name)
    });
  }

  function getLineData(data, name, isSpace = false) {
    const result = data?.map((_, index) => {
      const dateIndex = Math.floor(index / 4);
      const lineIndex = index % 4;

      const item = data?.[index] || {};
      const lastItem = data?.[index - (4 - lineIndex)] || {};
      const nextItem = data?.[index + (4 - lineIndex)] || {};

      const offset = getOffset({ list, dateIndex, name, max });
      const nextOffset = getOffset({ list, dateIndex: dateIndex + 1, name, max });
      let spaceValue;
      let value = item.radioValue - offset;

      switch (lineIndex) {
        case 0:
          spaceValue = offset;
          break;
        case 1:
          spaceValue = offset;
          if (!nextItem?.radioValue) {
            value = undefined;
          }
          break;
        case 2:
          spaceValue = (nextOffset + offset) / 2;
          value = (nextItem.radioValue + item.radioValue) / 2 - spaceValue;
          break;
        case 3:
          spaceValue = nextOffset;
          value = nextItem.radioValue - nextOffset;
          if (!lastItem?.radioValue) {
            value = undefined;
          }
          break;
      }
      if (!lastItem?.radioValue && !nextItem?.radioValue) {
        value = undefined;
      }
      // console.log(lineIndex, item, offset, nextOffset, spaceValue, value);
      const newItem = {
        ...item,
        value: isSpace ? spaceValue : value
      };
      return newItem;
    });
    // console.log('result', result);
    return result;
  }

  return [...spaceLineSeries, ...lineSeries];
}

完整代码

var dom = document.getElementById('chart-container');
var myChart = echarts.init(dom, null, {
  renderer: 'canvas',
  useDirtyRect: false
});
var app = {};

var option;

const defaultData = [
  {
    date: '2022年02月',
    list: [
      {
        name: '安列克-常州四药',
        value: 48196
      },
      {
        name: '贝克宁-成都贝特',
        value: 85944
      },
      {
        name: '瀚宝-深圳瀚宇',
        value: 43122
      },
      {
        name: '卡贝缩宫素-杭州澳亚',
        value: 46082
      },
      {
        name: '卡贝缩宫素-天吉生物',
        value: 28473
      },
      {
        name: '卡贝缩宫素-星银药业',
        value: 20584
      }
    ]
  },
  {
    date: '2022年03月',
    list: [
      {
        name: '安列克-常州四药',
        value: 97775
      },
      {
        name: '贝克宁-成都贝特',
        value: 134262
      },
      {
        name: '瀚宝-深圳瀚宇',
        value: 102538
      },
      {
        name: '卡贝缩宫素-杭州澳亚',
        value: 77479
      },
      {
        name: '卡贝缩宫素-天吉生物',
        value: 59422
      },
      {
        name: '卡贝缩宫素-星银药业',
        value: 32413
      }
    ]
  },
  {
    date: '2022年04月',
    list: [
      {
        name: '安列克-常州四药',
        value: 91399
      },
      {
        name: '贝克宁-成都贝特',
        value: 151064
      },
      {
        name: '瀚宝-深圳瀚宇',
        value: 74733
      },
      {
        name: '卡贝缩宫素-杭州澳亚',
        value: 75197
      },
      {
        name: '卡贝缩宫素-天吉生物',
        value: 46853
      },
      {
        name: '卡贝缩宫素-星银药业',
        value: 24845
      }
    ]
  },
  {
    date: '2022年05月',
    list: [
      {
        name: '安列克-常州四药',
        value: 83667
      },
      {
        name: '贝克宁-成都贝特',
        value: 114716
      },
      {
        name: '瀚宝-深圳瀚宇',
        value: 57327
      },
      {
        name: '卡贝缩宫素-杭州澳亚',
        value: 62267
      },
      {
        name: '卡贝缩宫素-天吉生物',
        value: 38604
      },
      {
        name: '卡贝缩宫素-星银药业',
        value: 19766
      }
    ]
  },
  {
    date: '2022年06月',
    list: [
      {
        name: '安列克-常州四药',
        value: 80524
      },
      {
        name: '贝克宁-成都贝特',
        value: 155227
      },
      {
        name: '瀚宝-深圳瀚宇',
        value: 67098
      },
      {
        name: '卡贝缩宫素-杭州澳亚',
        value: 61857
      },
      {
        name: '卡贝缩宫素-天吉生物',
        value: 44098
      },
      {
        name: '卡贝缩宫素-星银药业',
        value: 26956
      }
    ]
  },
  {
    date: '2022年07月',
    list: [
      {
        name: '安列克-常州四药',
        value: 92172
      },
      {
        name: '贝克宁-成都贝特',
        value: 118129
      },
      {
        name: '瀚宝-深圳瀚宇',
        value: 61548
      },
      {
        name: '卡贝缩宫素-杭州澳亚',
        value: 64490
      },
      {
        name: '卡贝缩宫素-天吉生物',
        value: 38073
      },
      {
        name: '卡贝缩宫素-星银药业',
        value: 21705
      }
    ]
  },
  {
    date: '2022年08月',
    list: [
      {
        name: '安列克-常州四药',
        value: 94615
      },
      {
        name: '贝克宁-成都贝特',
        value: 119397
      },
      {
        name: '瀚宝-深圳瀚宇',
        value: 60547
      },
      {
        name: '卡贝缩宫素-杭州澳亚',
        value: 73835
      },
      {
        name: '卡贝缩宫素-天吉生物',
        value: 37406
      },
      {
        name: '卡贝缩宫素-星银药业',
        value: 26228
      }
    ]
  }
]

function formatMoney(money) {
   return money
}

function run({ data = defaultData, height = 500 }) {
  const chartHeight = height;
  const maxY = 100;
  const maxHeight = chartHeight - maxY;

  function createData(initData = []) {
    const list = initData?.map((item) => ({
      ...item,
      total: item.list.reduce((pre, cur) => pre + cur.value, 0),
      list: item.list?.sort((a, b) => a.value - b.value)
    }));
    const legendData = [];
    const xAxisData = [];
    const seriesDataMap = {};
    let max = 0;

    // 生成x轴、图例数据
    for (const dateIndex in list) {
      const item = list[dateIndex];
      xAxisData.push(item.date);
      if (dateIndex < list?.length - 1) {
        new Array(3).fill(0).forEach((_, lineIndex) => {
          xAxisData.push(`line-${lineIndex}`);
        });
      }
      max = Math.max(max, item.total);
      for (const index in item.list) {
        const dataItem = item.list[index];
        if (!legendData?.includes(dataItem.name)) {
          legendData.push(dataItem.name);
        }
      }
    }

    // 根据图例生成数据
    for (const index in list) {
      const item = list[index];
      for (const name of legendData) {
        const dataItem = item?.list?.find((dataItem) => dataItem.name === name);
        _.set(seriesDataMap, `${name}.${index}`, dataItem?.value);
      }
    }

    const result = { list, legendData, xAxisData, seriesDataMap, max };
    // console.log('result', result);
    return result;
  }

  function createLineChart({ seriesData = [], initDataResult }) {
    const { list, max } = initDataResult;
    const spaceLineSeries = [];
    const lineSeries = [];
    // console.log('seriesData', seriesData);
    for (const seriesIndex in seriesData) {
      const seriesItem = seriesData[seriesIndex];
      const defaultLineSeries = {
        type: 'line',
        name: seriesItem.name,
        stack: `Line-${seriesIndex}`,
        smooth: 0.3,
        lineStyle: {
          width: 0,
          opacity: 0
        },
        symbol: 'none',
        showSymbol: false,
        triggerLineEvent: true,
        silent: true,
        areaStyle: {},
        emphasis: {
          focus: 'series'
        }
      };

      spaceLineSeries.push({
        ...defaultLineSeries,
        areaStyle: {
          opacity: 0
        },
        data: getLineData(seriesItem?.data, seriesItem.name, true)
      });

      lineSeries.push({
        ...defaultLineSeries,
        data: getLineData(seriesItem?.data, seriesItem.name)
      });
    }

    function getLineData(data, name, isSpace = false) {
      const result = data?.map((_, index) => {
        const dateIndex = Math.floor(index / 4);
        const lineIndex = index % 4;

        const item = data?.[index] || {};
        const lastItem = data?.[index - (4 - lineIndex)] || {};
        const nextItem = data?.[index + (4 - lineIndex)] || {};

        const offset = getOffset({ list, dateIndex, name, max });
        const nextOffset = getOffset({ list, dateIndex: dateIndex + 1, name, max });
        let spaceValue;
        let value = item.radioValue - offset;

        switch (lineIndex) {
          case 0:
            spaceValue = offset;
            break;
          case 1:
            spaceValue = offset;
            if (!nextItem?.radioValue) {
              value = undefined;
            }
            break;
          case 2:
            spaceValue = (nextOffset + offset) / 2;
            value = (nextItem.radioValue + item.radioValue) / 2 - spaceValue;
            break;
          case 3:
            spaceValue = nextOffset;
            value = nextItem.radioValue - nextOffset;
            if (!lastItem?.radioValue) {
              value = undefined;
            }
            break;
        }
        if (!lastItem?.radioValue && !nextItem?.radioValue) {
          value = undefined;
        }
        // console.log(lineIndex, item, offset, nextOffset, spaceValue, value);
        const newItem = {
          ...item,
          value: isSpace ? spaceValue : value
        };
        return newItem;
      });
      // console.log('result', result);
      return result;
    }

    return [...spaceLineSeries, ...lineSeries];
  }

  function createOption(initData) {
    const initDataResult = createData(initData);
    const { list, legendData, xAxisData, seriesDataMap, max } = initDataResult;
    const seriesData = [];

    for (const seriesIndex in Object.keys(seriesDataMap)) {
      const name = Object.keys(seriesDataMap)[seriesIndex];
      const data = seriesDataMap[name];
      seriesData.push({
        name,
        type: 'scatter',
        symbol: 'rect',
        z: 3,
        itemStyle: {
          opacity: 1
        },
        label: {
          show: true,
          color: '#fff',
          formatter: (params) => formatMoney(params.data.realValue, 0)
        },
        tooltip: {
          trigger: 'item',
          formatter: (params) => {
            return `<div>
            <div>年度月份:${params.name}</div>
            <div>${params.seriesName}${formatMoney(params.data.realValue, 0)}</div>
          </div>`;
          }
        },
        data: getChartData({ data, name })
      });
    }

    function getChartData({ data = [], name }) {
      const dataResult = [];
      data?.forEach((value, dateIndex) => {
        const y = maxY * (value / max);
        const ySize = maxHeight * (y / maxY);
        const offset = getOffset({ list, dateIndex, name, max });
        const radioValue = y + offset > 100 ? 100 : y + offset;

        dataResult.push({
          name,
          value: radioValue,
          radioValue,
          realValue: value,
          symbolOffset: [0, '50%'],
          symbolSize: [50, ySize]
        });

        if (dateIndex < data?.length - 1) {
          new Array(3).fill(0).forEach((_, lineIndex) => {
            dataResult.push({
              value: '',
              radioValue,
              realValue: value,
              isLine: true,
              lineIndex
            });
          });
        }
      });
      return dataResult;
    }

    const lineSeries = createLineChart({ seriesData, initDataResult });

    return {
      option: {
        legend: {
          data: legendData
        },
        xAxis: {
          data: xAxisData,
          axisTick: {
            show: false
          }
        },
        series: [...seriesData, ...lineSeries]
      }
    };
  }

  function getOffset({ list, dateIndex, name, max }) {
    const dateData = list[dateIndex]?.list || [];
    const itemIndex = dateData?.findIndex((item) => item.name === name);

    let offset = 0;
    for (let i = 0; i < itemIndex; i++) {
      const itemValue = dateData[i].value;
      offset += maxY * (itemValue / max);
    }

    return offset;
  }

  const { option: newOption } = createOption(data);

  return _.merge(
    {
      grid: {
        top: 40,
        left: 20,
        right: 20,
        bottom: 40,
        containLabel: true
      },
      yAxis: {
        show: false,
        max: maxY
      },
      tooltip: {
        // show: true,
        // trigger: 'axis',
        // axisPointer: {
        //   type: 'none'
        // },
        // formatter: (params, ticket) => {
        //   // console.log('params', params, ticket);
        //   return '';
        // }
      },
      dataZoom: [
        {
          type: 'slider',
          filterMode: 'weakFilter',
          showDataShadow: false,
          showDetail: false,
          brushSelect: false,
          height: 20,
          bottom: 10,
          startValue: 1,
          endValue: 5,
          xAxisIndex: 0,
          start: 0,
          end: 100
        }
      ],
      xAxis: {
        type: 'category',
        data: newOption.xAxis.data,
        axisLabel: {
          formatter: function (value) {
            return value?.includes('line') ? '' : value;
          }
        }
      }
    },
    newOption
  );
}

function getOption(data, height) {
  return run({ data, height });
}

option = getOption(defaultData);

if (option && typeof option === 'object') {
  myChart.setOption(option);
}

window.addEventListener('resize', myChart.resize);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/558576.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

给rwkv-pytorch 写个chat ui demo

rwkv-pytorch 项目地址 rwkv-pytorch from nicegui import uimessage_dict {1: [{"name":"Assistant","text":"你好"}]} current_name 1 import aiohttpasync def get_text_async(text"Hello, how are you?"):# 定义AP…

统一SQL-支持CHAR和VARCHAR2 (size BYTE|CHAR)转换

统一SQL介绍 https://www.light-pg.com/docs/LTSQL/current/index.html 源和目标 源数据库&#xff1a;Oracle 目标数据库&#xff1a;Postgresql&#xff0c;TDSQL-MySQL&#xff0c;达梦8&#xff0c;LightDB-Oracle 操作目标 在Oracle中的CHAR和VARCHAR2数据类型&…

stm32二刷-GPIO

一 什么是 GPIO: GPIO(general porpose intput output), 通用输入输出端口 . 二 我们先认识芯片控制 GPIO 输出控制。 2.1LED 硬件原理如图&#xff1a; 当电流从这根电线流通&#xff0c; LED 亮。当电流不通过这根电线&#xff0c; LED 灭。 上面 PF** &#xff0c;芯片电…

Windows下使用SDKMAN对JDK(Java)进行多版本管理

Windows下使用SDKMAN对JDK&#xff08;Java&#xff09;进行多版本管理 1.背景2.基于msys2工具2.1. msys2简介2.2. 安装与配置2.2. Windows环境变量配置参考2.3 结果确认 3. 基于WSL 1.背景 前端有nvm,python有miniconda,miniforge等&#xff0c;java呢&#xff1f;java在Linu…

【机器学习】Q-Learning算法:在序列决策问题中的实践与探索

在序列决策问题中的实践与探索 一、Q-Learning算法概述二、Q-Learning算法实例分析三、Q-Learning算法代码实现四、总结与展望 在人工智能领域&#xff0c;序列决策问题一直是一个核心挑战。面对复杂的环境和动态变化的状态&#xff0c;智能体如何做出最优决策&#xff0c;以达…

电动汽车充电站的最优选址定容(matlab程序)

0.代码链接 电动汽车充电站的最优选址定容&#xff08;matlab程序&#xff09;_电动汽车充电站配置程序资源-CSDN文库 1.简述 随着经济的快速发展,环境污染和能源紧缺问题越来越严重,电动汽车的出现可以起到保护环境,节约能源的作用。未来随着我国电动汽车数量的快速增加,充电…

OpenHarmony实战开发-如何视频弹幕功能。

介绍 本示例介绍如何使用ohos.danmakuflamemaster和ohos.gsyvideoplayer开发支持视频弹幕的播放器。可以自定义弹幕样式、占据屏幕宽度&#xff0c;发送弹幕&#xff0c;开关弹幕视图。 效果图预览 使用说明 点击播放按钮&#xff0c;进行视频播放&#xff0c;弹幕自动开启点…

Linux的学习之路:14、文件(1)

摘要 有一说一文件一天学不完&#xff0c;细节太多了&#xff0c;所以这里也没更新完&#xff0c;这里部分文件知识&#xff0c;然后C语言和os两种的文件操作 目录 摘要 一、文件预备 二、c文件操作 三、OS文件操作 1、系统文件I/O 2、接口介绍 四、思维导图 一、文件…

基于STM32实现流水灯【Proteus仿真】

详情更多 wechat&#xff1a;嵌入式工程师成长日记 https://mp.weixin.qq.com/s?__bizMzg4Mzc3NDUxOQ&mid2247485624&idx1&sn4e553234c2624777409bd2067a07aad8&chksmcf430de0f83484f6189b119d9d83ea6e6f2a85d13afaa04d218483918231c38e6382d3007061&tok…

nginx-ingress详解

一、ingress概述 1、概述 Kubernetes是一个拥有强大故障恢复功能的集群&#xff0c;当pod挂掉时&#xff0c;集群会重新创建一个pod出来&#xff0c;但是pod的IP也会随之发生变化&#xff0c;为了应对这种情况&#xff0c;引入了service&#xff0c;通过service的标签匹配&am…

aspx页面 ASP.NET Web Forms中的DropDownList添加搜索功能使用select2

.NET兼职社区 select2依赖jquery JS直接去官网下载&#xff1a;https://select2.org/getting-started/basic-usage或者https://www.bootcdn.cn/ <% Page Title"Home Page" Language"C#" MasterPageFile"~/Site.Master" AutoEventWireup&qu…

uniapp_微信小程序_预约时间组件的使用

一、官方文档 DatetimePicker 选择器 | uView 2.0 - 全面兼容 nvue 的 uni-app 生态框架 - uni-app UI 框架 (uviewui.com) 二、完成的效果 之前使用的是Calendar 日历 这个太耗性能了&#xff0c;直接页面卡顿&#xff0c;所以就换成以上选择器了 三、代码 <u-datetime-p…

Spring Boot集成fastdfs快速入门Demo

1.什么是fastdfs FastDFS 是一个开源的高性能分布式文件系统&#xff08;DFS&#xff09;。它的主要功能包括&#xff1a;文件存储&#xff0c;文件同步和文件访问&#xff0c;以及高容量和负载平衡。主要解决了海量数据存储问题&#xff0c;特别适合以中小文件&#xff08;建议…

UDP文件传输工具之UDP怎么限流

UDP&#xff08;用户数据报协议&#xff09;以其低延迟和高速度的特点&#xff0c;在实时应用和大数据传输中扮演着重要角色。然而&#xff0c;UDP作为一种无连接的协议&#xff0c;并不保证数据包的顺序、完整性或可靠性。 因此&#xff0c;企业在寻求一种方式&#xff0c;有…

PCA(Principal Component Analysis,主成分分析)与矩阵X的协方差矩阵之间的联系

PCA&#xff08;Principal Component Analysis&#xff0c;主成分分析&#xff09;是一种常用的降维技术&#xff0c;用于将高维数据集投影到低维空间中。在PCA中&#xff0c;投影方程将原始特征向量 ( x 1 , x 2 , … , x p ) (x_1, x_2, \ldots, x_p) (x1​,x2​,…,xp​)映射…

服务器基本故障和排查方法

前言 服务器运维工作中遇到的问题形形色色&#xff0c;无论何种故障&#xff0c;都需要结合具体情况&#xff0c;预防为主的思想&#xff0c;熟悉各种工具和技术手段&#xff0c;养成良好的日志分析习惯&#xff0c;同时建立完善的应急预案和备份恢复策略&#xff0c;才能有效…

45、二叉树-二叉树的右视图

思路 层序遍历 从左向右遍历每一层取最后一个数&#xff0c;代码如下&#xff1a; public List<Integer> rightSideView(TreeNode root) {if (rootnull){return new ArrayList<>();}Queue<TreeNode> queue new LinkedList<>();List<Integer> …

Unity 中(提示框Tweet)

using UnityEngine; using UnityEngine.UI; using DG.Tweening; using System; public class Message : MonoBehaviour {public float dropDuration 0.5f; // 掉落持续时间public float persisterDuration 1f; // 持续显示时间public float dorpHeight;public static Message…

鸿蒙系列--ArkTS

一、ArkUI开发框架 ArkUI框架提供开发者两种开发方式&#xff1a;基于ArkTS的声明式开发范式和基于JS扩展的类Web开发范式。声明式开发范式更加简洁&#xff0c;类 Web 开发范式对 Web 及前端开发者更友好 二、ArkTS声明式开发范式 对比类 Web 开发范式代码更为精简&#xf…

用FRP配置toml文件搭建内网穿透

需求场景 1、一台外网可访问的有固定ip的云服务器&#xff0c;Ubuntu系统 2、一台外网无法访问的无固定ip的本地家用电脑&#xff0c;Ubuntu系统 需求&#xff1a;将云服务器搭建为一台内网穿透服务器&#xff0c;实现通过外网访问家用电脑&#xff08;网页&#xff09;的功能。…
最新文章