Echarts实现大屏可视化

一、效果展示

二、简介

该项目涉及到的图表有:

  • 渐变堆叠面积图
  • 中国地图
  • 涟漪特效散点图
  • 饼图
  • 横向柱状图
  • 竖向柱状图
  • 圆环饼图

该项目主要展示的是使用Echarts制作的大屏可视化,所用到的技术有:

2.1 前端:

        vue3、vite、echarts、pinia、sass、websocket、vue-router

2.2 后端:

        nodejs、koa、mysql、websocket、cors

 2.3 功能描述:

该项目是使用websocket实现数据的实时推送,每一个图表就是一个单独的组件,该组件可以进行全屏和取消全屏的操作,可以实现多个浏览器同时访问http://localhost:5173/screen的时候一个人的操作所有人都可以看见,可以实现全局图表主题的切换,主题的切换也是可以实现多个浏览器之间联动的。并且随着浏览器窗口的变化,所有的图表都可以自动适配屏幕的大小。

2.3.1 商品销量趋势图表

点击图标的标题可以进行 商品销量趋势、地区销量趋势、商家销量趋势 三个图表之间的来回切换。

2.3.2 热销商品销售金额占比统计图表

点击该组件的左右箭头可以实现热销商品中 手机数码、美妆护肤、女装 三个图表之间的来回切换。

2.3.3 商家销售统计图表

该组件显示的图表并不是一次性将所有的商家数据进行展示,而是按照销售数量从小到大进行排序,然后每次先显示5条数据,每隔3s切换一次数据。

2.3.4 地区销售排行图表

该组件显示的图表并不是一次性将所有的地区数据进行展示,而是按照销售数量从大到小进行排序,然后每次先显示10条数据,每隔2s将数据向左移动一个。

三、代码展示

3.1 热销商品销售金额占比统计表

<template>
  <div class="com-container">
    <div
      class="com-chart"
      ref="sellerRef"
    ></div>
    <!-- 左右按钮 -->
    <div class="icon-group">
      <span
        class="iconfont"
        :style="{ fontSize: fontSize + 'px', color: theme.color }"
        @click="handleLeft"
      >&#xe6eb;</span>
      <span
        class="iconfont"
        :style="{ fontSize: fontSize + 'px', color: theme.color }"
        @click="handleRight"
      >&#xe6ee;</span>
    </div>
    <div
      class="title"
      :style="{ fontSize: fontSize / 2 + 'px', color: theme.color }"
    >{{ currentType.name }}</div>
  </div>
</template>

<script setup>
import { computed, onBeforeUnmount, onMounted, ref, shallowRef, watch, } from 'vue'
import api from '@/utils/url'
import { get } from '@/utils/request'
import * as echarts from 'echarts'
// 引入仓库
import { useChart } from '@/store'
import { getThemeValue } from '@/utils/theme'
import Socket from '@/utils/socket'
const theme = computed(() => getThemeValue(store.theme))
const store = useChart()
const sellerRef = ref(null)//图表容器
const chartInstance = shallowRef(null)//图表实例
const currentType = ref({})
const fontSize = ref()
const handleLeft = () => {
  const index = allData.value.findIndex(item => item.name === currentType.value.name)
  if (index - 1 >= 0) {
    currentType.value = allData.value[index - 1]
  } else {
    currentType.value = allData.value[allData.value.length - 1]
  }
  updateChart()
}
const handleRight = () => {
  const index = allData.value.findIndex(item => item.name === currentType.value.name)
  if (index + 1 <= allData.value.length - 1) {
    currentType.value = allData.value[index + 1]
  } else {
    currentType.value = allData.value[0]
  }
  updateChart()
}
// 监听主题的切换
watch(() => store.theme, () => {
  // 一旦主题切换了就将原来的图表销毁掉
  chartInstance.value.dispose()
  initChart()// 重新以最新的主题进行初始化图表
  screenAdapter()//完成屏幕适配
  updateChart()//更新图表
})
// 初始化柱状图实例化对象
const initChart = () => {
  chartInstance.value = echarts.init(sellerRef.value, store.theme)
  // 对图标初始化的配置
  const initOption = {
    // 标题配置
    title: {
      text: '▎热销商品销售金额占比统计',
      left: 20,//设置标题距离左边的位置
      top: 20//设置标题距离上边的位置
    },
    // 提示框配置
    tooltip: {
      trigger: 'item',// 鼠标移入到坐标轴的时候触发提示框
      formatter: (val) => {
        let total = 0
        // 三级分类
        const thirdCategory = val.data.children
        // 计算出所有三级分类的数值总和
        thirdCategory.map(item => {
          total += item.value
        })
        let str = ''
        thirdCategory.forEach(item => {
          str += `${item.name}: ${(item.value / total * 100).toFixed()}%<br>`
        })
        return str
      }
    },
    // 图例配置
    legend: {
      top: '15%',
      icon: 'circle',//图标类型
    },
    // 图表类型
    series: [
      {
        type: 'pie',
        label: {
          show: false,//是否显示标签
        },
        emphasis: {//鼠标移入到饼图上的时候
          label: {
            show: true
          },
          labelLine: {//标签线
            show: false//折线
          }
        }
      }
    ]
  }
  chartInstance.value.setOption(initOption)
}
const allData = ref([])
// 获取服务器数据
const getData = async (res) => {
  // const res = await get(api.hot)
  allData.value = res
  currentType.value = res[0]
  updateChart()
}
// 在组件创建完成之后,进行回调函数的注册
Socket.registerCallback('hotData', getData)
// 更新图表
const updateChart = () => {
  const { children, name } = currentType.value
  // 图表配置项
  const option = {
    // 图例配置
    legend: {
      data: children.map(item => item.name),
    },
    // 图表类型
    series: [
      {
        data: children.map(item => (
          {
            name: item.name,
            value: item.value,
            children: item.children
          }
        )),
      }
    ]
  }
  chartInstance.value.setOption(option)
}
// 屏幕适配【当浏览器的大小发生变化时,来完成屏幕的适配】
const screenAdapter = () => {
  const titleFontSize = sellerRef.value.offsetWidth / 100 * 3.6
  fontSize.value = titleFontSize * 2
  // 和分辨率大小相关的配置项
  const adapterOption = {
    title: {
      textStyle: {
        fontSize: titleFontSize
      },
    },
    legend: {
      // 设置图标的大小和文字大小
      itemWidth: titleFontSize,
      itemHeight: titleFontSize,
      textStyle: {
        fontSize: titleFontSize
      },
    },
    series: [
      {
        radius: titleFontSize * 4.5,// 设置饼图的半径大小
        center: ['50%', '60%'],// 设置饼图的位置
      }
    ]
  }
  chartInstance.value.setOption(adapterOption)
  // 手动调用图标对象的resize方法,才能生效
  chartInstance.value.resize()
}
// 将当前组件中的变量暴露出去,让父组件进行调用
defineExpose({ screenAdapter })
onMounted(() => {
  initChart()
  // getData()
  // 发送数据给服务器,告诉服务器,我现在需要数据
  Socket.send({
    action: 'getData',
    socketType: 'hotData',
    chartName: 'hot',
    value: ''
  })
  // 监听窗口大小变化事件
  window.addEventListener('resize', screenAdapter)
  // 在页面加载完成的时候,主动进行屏幕的适配 
  screenAdapter()
})
onBeforeUnmount(() => {
  // 在页面加载完成的时候,主动进行屏幕的适配
  window.removeEventListener('resize', screenAdapter)
  // 在组件销毁的时候,进行回调函数的注销
  Socket.unregisterCallback('hotData')
})
</script>
<style lang="scss" scoped>
.icon-group {
  position: absolute;
  left: 10px;
  right: 10px;
  top: 50%;
  display: flex;
  justify-content: space-between;
  z-index: 1;
  color: #fff;
  .iconfont {
    cursor: pointer;
  }
}
.title {
  position: absolute;
  left: 80%;
  bottom: 20px;
  z-index: 1;
  color: #fff;
}
</style>

3.2 商家分布表

<template>
  <div
    class="com-container"
    @dblclick="revertChainMap"
  >
    <div
      class="com-chart"
      ref="sellerRef"
    ></div>
  </div>
</template>

<script setup>
import { onBeforeUnmount, onMounted, ref, shallowRef, watch, } from 'vue'
import api from '@/utils/url'
import { get } from '@/utils/request'
import * as echarts from 'echarts'
import getMapData from '@/utils/getMapData'
// 引入仓库
import { useChart } from '@/store'
import Socket from '@/utils/socket'

const store = useChart()

const sellerRef = ref(null)//图表容器
const chartInstance = shallowRef(null)//图表实例
const mapData = ref({})//所获取的省份的矢量数据

// 监听主题的切换
watch(() => store.theme, () => {
  // 一旦主题切换了就将原来的图表销毁掉
  chartInstance.value.dispose()
  initChart()// 重新以最新的主题进行初始化图表
  screenAdapter()//完成屏幕适配
  updateChart()//更新图表
})
// 初始化柱状图实例化对象
const initChart = async () => {
  chartInstance.value = echarts.init(sellerRef.value, store.theme)
  // 获取中国地图的矢量数据
  const chinaMap = await getMapData('中国')
  echarts.registerMap('china', chinaMap)
  // 对图标初始化的配置
  const initOption = {
    // 标题配置
    title: {
      text: '▎商家分布',
      left: 20,
      top: 20
    },
    // 地图配置
    geo: {
      type: 'map',
      map: 'china',
      top: '5%',
      bottom: '5%',
      itemStyle: {
        areaColor: '#2E72BF',//设置地图的背景颜色
        borderColor: '#333',//设置地图的边框颜色【每个省份之间边界线的颜色】
      }
    },
    legend: {
      left: '5%',
      bottom: '5%',
      orient: 'vertical'//垂直显示
    }
  }
  chartInstance.value.setOption(initOption)
  // 对地图点击事件的监听
  chartInstance.value.on('click', async params => {
    if (!mapData.value[params.name]) {
      // 获取对应省份的矢量数据
      const county = await getMapData(params.name)
      mapData.value[params.name] = county
      // 注册地图的矢量数据
      echarts.registerMap(params.name, county)
    }
    // 切换地图的显示
    const changeOption = {
      geo: {
        map: params.name
      }
    }
    chartInstance.value.setOption(changeOption)
  })
}

const allData = ref([])
// 获取服务器数据
const getData = async (res) => {
  // const res = await get(api.map)
  allData.value = res
  updateChart()
}// 在组件创建完成之后,进行回调函数的注册
Socket.registerCallback('mapData', getData)
// 更新图表
const updateChart = () => {
  // 图表配置项
  const option = {
    // 图例
    legend: {
      data: allData.value.map(item => item.name)
    },
    // 图表类型
    /* 
      返回的对象就代表的是一个类别下的所有散点数据
      如果想在地图中显示散点的数据,所以需要给散点的图标增加一个配置,coordinateSystem: 'geo'
    */
    series: allData.value.map(item => ({
      type: 'effectScatter',//散点类型【涟漪】
      rippleEffect: {//涟漪效果
        scale: 5,//散点涟漪范围的大小
        brushType: 'stroke',//涟漪效果的形状【stroke:空心,fill:实心】
      },
      name: item.name,
      data: item.children,
      coordinateSystem: 'geo',
    })
    ),
  }
  chartInstance.value.setOption(option)
}
// 屏幕适配【当浏览器的大小发生变化时,来完成屏幕的适配】
const screenAdapter = () => {
  const titleFontSize = sellerRef.value.offsetWidth / 100 * 3.6
  // 和分辨率大小相关的配置项
  const adapterOption = {
    title: {
      textStyle: {
        fontSize: titleFontSize
      }
    },
    legend: {
      // 设置图标的大小和文字大小
      itemWidth: titleFontSize / 2,
      itemHeight: titleFontSize / 2,
      itemGap: titleFontSize / 2,
      textStyle: {
        fontSize: titleFontSize / 2
      },
    }
  }
  chartInstance.value.setOption(adapterOption)
  // 手动调用图标对象的resize方法,才能生效
  chartInstance.value.resize()
}
// 将当前组件中的变量暴露出去,让父组件进行调用
defineExpose({ screenAdapter })
// 回到中国地图
const revertChainMap = () => {
  chartInstance.value.setOption({
    geo: {
      map: 'china'
    }
  })
}
onMounted(() => {
  initChart()
  // getData()
  // 发送数据给服务器,告诉服务器,我现在需要数据
  Socket.send({
    action: 'getData',
    socketType: 'mapData',
    chartName: 'map',
    value: ''
  })
  // 监听窗口大小变化事件
  window.addEventListener('resize', screenAdapter)
  // 在页面加载完成的时候,主动进行屏幕的适配 
  screenAdapter()
})
onBeforeUnmount(() => {
  // 在页面加载完成的时候,主动进行屏幕的适配
  window.removeEventListener('resize', screenAdapter)
  // 在组件销毁的时候,进行回调函数的注销
  Socket.unregisterCallback('mapData')
})
</script>

3.3 地区销售排行表

<template>
  <div class="com-container">
    <div
      class="com-chart"
      ref="sellerRef"
    ></div>
  </div>
</template>

<script setup>
import { onBeforeUnmount, onMounted, ref, shallowRef, watch, } from 'vue'
import api from '@/utils/url'
import { get } from '@/utils/request'
import * as echarts from 'echarts'
// 引入仓库
import { useChart } from '@/store'
import Socket from '@/utils/socket'

const store = useChart()
const sellerRef = ref(null)//图表容器
const chartInstance = shallowRef(null)//图表实例
// 监听主题的切换
watch(() => store.theme, () => {
  // 一旦主题切换了就将原来的图表销毁掉
  chartInstance.value.dispose()
  initChart()// 重新以最新的主题进行初始化图表
  screenAdapter()//完成屏幕适配
  updateChart()//更新图表
})
// 初始化柱状图实例化对象
const initChart = () => {
  chartInstance.value = echarts.init(sellerRef.value, store.theme)
  // 对图标初始化的配置
  const initOption = {
    // 标题配置
    title: {
      text: '▎地区销售排行',
      left: 20,//设置标题距离左边的位置
      top: 20//设置标题距离上边的位置
    },
    // 坐标轴四周的边距
    grid: {
      top: "40%",
      left: "5%",
      right: "5%",
      bottom: "5%",
      containLabel: true//距离是否包含坐标轴刻度标签
    },
    // x轴配置
    xAxis: { type: 'category', },
    // y轴配置
    yAxis: { type: 'value', },
    // 图表类型
    series: [
      {
        type: 'bar',
        label: {
          show: true,//是否显示数值
          position: "top",//数值的显示位置
          color: '#fff'//数值颜色
        }
      }
    ]
  }
  chartInstance.value.setOption(initOption)
  // 鼠标移入图表,停止定时器
  chartInstance.value.on('mouseover', () => {
    clearInterval(timer.value)
  })
  // 鼠标移出图表,恢复定时器
  chartInstance.value.on('mouseout', () => {
    startIntervalue()
  })
}

const allData = ref([])
// 获取服务器数据
const getData = async (res) => {
  // const res = await get(api.rank)
  // 对数据进行排序,从大到小
  allData.value = res.sort((a, b) => b.value - a.value)
  updateChart()
  startIntervalue()
}
// 在组件创建完成之后,进行回调函数的注册
Socket.registerCallback('rankData', getData)
const startValue = ref(0)//缩略轴的起始值
const endValue = ref(9)//缩略轴的结束值
// 更新图表
const updateChart = () => {
  const colorArr = [
    ["#0BA82C", "#4FF778"],
    ["#2E72BF", "#23E5E5"],
    ["#5052EE", "#AB6EE5"]
  ]
  const data = allData.value
  // 图表配置项
  const option = {
    // x轴配置
    xAxis: { data: data.map(item => item.name) },
    dataZoom: {
      show: false,//是否显示缩略轴
      startValue: startValue.value,//缩略轴的起始值
      endValue: endValue.value//缩略轴的结束值
    },
    // 图表类型
    series: [
      {
        data: data.map(item => item.value),
        itemStyle: {
          color: (params) => {
            let color = ''
            if (params.data > 300) {
              color = colorArr[0]
            } else if (params.data > 200) {
              color = colorArr[1]
            } else {
              color = colorArr[2]
            }
            // 从上到下渐变
            return new echarts.graphic.LinearGradient(0, 0, 0, 1, [
              { offset: 0, color: color[0] },//0%处的颜色
              { offset: 1, color: color[1] }//100%处的颜色
            ])
          }
        }
      }
    ]
  }
  chartInstance.value.setOption(option)
}
// 屏幕适配【当浏览器的大小发生变化时,来完成屏幕的适配】
const screenAdapter = () => {
  const titleFontSize = sellerRef.value.offsetWidth / 100 * 3.6
  // 和分辨率大小相关的配置项
  const adapterOption = {
    title: {
      textStyle: { fontSize: titleFontSize },
    },
    tooltip: {
      axisPointer: {
        lineStyle: { width: titleFontSize }
      }
    },
    series: [
      {
        barWidth: titleFontSize,
        itemStyle: {//柱状图样式
          borderRadius: [titleFontSize / 2, titleFontSize / 2, 0, 0],//柱状图圆角
        }
      }
    ]
  }
  chartInstance.value.setOption(adapterOption)
  // 手动调用图标对象的resize方法,才能生效
  chartInstance.value.resize()
}
// 将当前组件中的变量暴露出去,让父组件进行调用
defineExpose({ screenAdapter })
const timer = ref(null)
const startIntervalue = () => {
  timer.value && clearInterval(timer.value)
  timer.value = setInterval(() => {
    startValue.value++
    endValue.value++
    if (endValue.value > allData.value.length - 1) {
      startValue.value = 0
      endValue.value = 9
    }
    updateChart()
  }, 2000)
}
onMounted(() => {
  initChart()
  // getData()
  // 发送数据给服务器,告诉服务器,我现在需要数据
  Socket.send({
    action: 'getData',
    socketType: 'rankData',
    chartName: 'ranks',
    value: ''
  })
  // 监听窗口大小变化事件
  window.addEventListener('resize', screenAdapter)
  // 在页面加载完成的时候,主动进行屏幕的适配 
  screenAdapter()
})
onBeforeUnmount(() => {
  // 清除定时器
  clearInterval(timer.value)
  // 在页面加载完成的时候,主动进行屏幕的适配
  window.removeEventListener('resize', screenAdapter)
  Socket.unregisterCallback('rankData')
})
</script>

3.4 商家销量统计表

<template>
  <div class="com-container">
    <div
      class="com-chart"
      ref="sellerRef"
    ></div>
  </div>
</template>

<script setup>
import { onBeforeUnmount, onMounted, ref, shallowRef, watch, } from 'vue'
import api from '@/utils/url'
import { get } from '@/utils/request'
import * as echarts from 'echarts'
// 引入仓库
import { useChart } from '@/store'
import Socket from '@/utils/socket'

const store = useChart()
const sellerRef = ref(null)//图表容器
const chartInstance = shallowRef(null)//图表实例
// 监听主题的切换
watch(() => store.theme, () => {
  // 一旦主题切换了就将原来的图表销毁掉
  chartInstance.value.dispose()
  initChart()// 重新以最新的主题进行初始化图表
  screenAdapter()//完成屏幕适配
  updateChart()//更新图表
})
// 初始化柱状图实例化对象
const initChart = () => {
  chartInstance.value = echarts.init(sellerRef.value, store.theme)
  // 对图标初始化的配置
  const initOption = {
    // 标题配置
    title: {
      text: '▎商家销售统计',
      left: 20,//设置标题距离左边的位置
      top: 20//设置标题距离上边的位置
    },
    // 坐标轴四周的边距
    grid: {
      top: "20%",
      left: "3%",
      right: "6%",
      bottom: "3%",
      containLabel: true//距离是否包含坐标轴刻度标签
    },
    // x轴配置
    xAxis: {
      type: 'value',
    },
    // y轴配置
    yAxis: {
      type: 'category',
    },
    // 提示框配置
    tooltip: {
      trigger: 'axis',//鼠标移入到坐标轴的时候触发提示框
      axisPointer: {//指示器样式
        type: 'line',
        z: 0,
        lineStyle: {
          type: 'solid',
          color: "#2D3443"
        }
      }
    },
    // 图表类型
    series: [
      {
        type: 'bar',
        label: {
          show: true,//是否显示数值
          position: "right",//数值的显示位置
          color: '#fff'//数值颜色
        },
        itemStyle: {//柱状图样式
          /* 
            线性渐变:
              指明颜色渐变的方向
              指明不同百分比之下颜色的值
              0,0,1,0:表示两个坐标(0,0)(1,0)【从左到右渐变】
          */
          color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
            { offset: 0, color: '#5052EE' },//0%处的颜色
            { offset: 1, color: '#AB6EE5' }//100%处的颜色
          ])
        }
      }
    ]
  }
  chartInstance.value.setOption(initOption)

  // 对图表对象进行鼠标事件的监听,鼠标移入的时候停止更新,鼠标移除的时候恢复更新
  chartInstance.value.on('mouseover', () => {
    clearInterval(timer.value) // 将定时器进行取消
  })
  chartInstance.value.on('mouseout', () => {
    startInterval()// 重新开启定时器
  })
}

const allData = ref([])
const currentPage = ref(1)//当前页码
const totalPage = ref(0)//一共有多少页
// 获取服务器数据
const getData = async (res) => {
  // const res = await get(api.seller)
  // 对数据进行排序,从小到大
  allData.value = res.sort((a, b) => a.value - b.value)
  totalPage.value = Math.ceil(allData.value.length / 10)
  updateChart()
  // 启动定时器
  startInterval()
}
// 在组件创建完成之后,进行回调函数的注册
Socket.registerCallback('sellerData', getData)
// 更新图表
const updateChart = () => {
  const data = allData.value.slice((currentPage.value - 1) * 5, currentPage.value * 5)
  // 图表配置项
  const option = {
    // y轴配置
    yAxis: {
      data: data.map(item => item.name)
    },
    // 图表类型
    series: [
      {
        data: data.map(item => item.value),
      }
    ]
  }
  chartInstance.value.setOption(option)
}
// 定时器id
const timer = ref(null)
const startInterval = () => {
  // 先将之前的定时器清空之后再开启新的定时器
  timer.value && clearInterval(timer.value)
  timer.value = setInterval(() => {
    currentPage.value++
    if (currentPage.value > totalPage.value) {
      currentPage.value = 1
    }
    updateChart()
  }, 3000);
}
// 屏幕适配【当浏览器的大小发生变化时,来完成屏幕的适配】
const screenAdapter = () => {
  const titleFontSize = sellerRef.value.offsetWidth / 100 * 3.6
  // 和分辨率大小相关的配置项
  const adapterOption = {
    title: {
      textStyle: {
        fontSize: titleFontSize
      },
    },
    tooltip: {
      axisPointer: {
        lineStyle: {
          width: titleFontSize
        }
      }
    },
    series: [
      {
        barWidth: titleFontSize,
        itemStyle: {//柱状图样式
          borderRadius: [0, titleFontSize / 2, titleFontSize / 2, 0],//柱状图圆角
        }
      }
    ]
  }
  chartInstance.value.setOption(adapterOption)
  // 手动调用图标对象的resize方法,才能生效
  chartInstance.value.resize()
}
// 将当前组件中的变量暴露出去,让父组件进行调用
defineExpose({ screenAdapter })
onMounted(() => {
  initChart()
  // getData()
  // 发送数据给服务器,告诉服务器,我现在需要数据
  Socket.send({
    action: 'getData',
    socketType: 'sellerData',
    chartName: 'seller',
    value: ''
  })
  // 监听窗口大小变化事件
  window.addEventListener('resize', screenAdapter)
  // 在页面加载完成的时候,主动进行屏幕的适配 
  screenAdapter()
})
onBeforeUnmount(() => {
  clearInterval(timer.value)
  // 在页面加载完成的时候,主动进行屏幕的适配
  window.removeEventListener('resize', screenAdapter)
  Socket.unregisterCallback('sellerData')
})
</script>

3.5 库存和销量分析表

<template>
  <div class="com-container">
    <div
      class="com-chart"
      ref="sellerRef"
    ></div>
  </div>
</template>

<script setup>
import { onBeforeUnmount, onMounted, ref, shallowRef, watch, } from 'vue'
import api from '@/utils/url'
import { get } from '@/utils/request'
import * as echarts from 'echarts'
// 引入仓库
import { useChart } from '@/store'
import Socket from '@/utils/socket'

const store = useChart()
const sellerRef = ref(null)//图表容器
const chartInstance = shallowRef(null)//图表实例
// 监听主题的切换
watch(() => store.theme, () => {
  // 一旦主题切换了就将原来的图表销毁掉
  chartInstance.value.dispose()
  initChart()// 重新以最新的主题进行初始化图表
  screenAdapter()//完成屏幕适配
  updateChart()//更新图表
})
// 初始化柱状图实例化对象
const initChart = () => {
  chartInstance.value = echarts.init(sellerRef.value, store.theme)
  // 对图标初始化的配置
  const initOption = {
    // 标题配置
    title: {
      text: '▎库存和销量分析',
      left: 20,//设置标题距离左边的位置
      top: 20//设置标题距离上边的位置
    },
  }
  chartInstance.value.setOption(initOption)
  // 对图表对象进行鼠标事件的监听,鼠标移入的时候停止更新,鼠标移除的时候恢复更新
  chartInstance.value.on('mouseover', () => {
    clearInterval(timer.value) // 将定时器进行取消
  })
  chartInstance.value.on('mouseout', () => {
    startInterval()// 重新开启定时器
  })
}

const allData = ref([])
const currentPage = ref(1)//当前页码
const totalPage = ref(0)//一共有多少页
// 获取服务器数据
const getData = async (res) => {
  // const res = await get(api.stock)
  allData.value = res
  totalPage.value = Math.ceil(allData.value.length / 5)
  updateChart()
  // 启动定时器
  startInterval()
}
Socket.registerCallback('stockData', getData)
// 更新图表
const updateChart = () => {
  const data = allData.value.slice((currentPage.value - 1) * 5, currentPage.value * 5)
  const centerArr = [
    ['18%', '40%'],
    ['50%', '40%'],
    ['82%', '40%'],
    ['34%', '75%'],
    ['66%', '75%']
  ]
  const colorArr = [
    ['#4FF778', '#0BA82C'],
    ['#E5DD45', '#E8B11C'],
    ['#E8821C', '#E55445'],
    ['#5052EE', '#AB6EE5'],
    ['#23E5E5', '#2E72BF']
  ]
  // 图表配置项
  const option = {
    // 图表类型
    series: data.map((item, index) => ({
      type: 'pie',
      center: centerArr[index],//饼图中心点的坐标
      emphasis: { scale: false },//关闭鼠标移入时的动画效果
      labelLine: {
        show: false//隐藏指示线
      },
      label: {
        position: 'center',
        color: colorArr[index][0]
      },
      data: [
        {
          value: item.sales,
          itemStyle: {
            // 从下到上渐变
            color: new echarts.graphic.LinearGradient(0, 1, 0, 0, [
              { offset: 0, color: colorArr[index][0] },//0%处的颜色
              { offset: 1, color: colorArr[index][1] }//100%处的颜色
            ])
          }
        },
        {
          name: item.name + '\n\n' + item.sales,
          value: item.stock,
          itemStyle: {
            color: '#333843'
          }
        }
      ]
    }))
  }
  chartInstance.value.setOption(option)
}
// 定时器id
const timer = ref(null)
const startInterval = () => {
  // 先将之前的定时器清空之后再开启新的定时器
  timer.value && clearInterval(timer.value)
  timer.value = setInterval(() => {
    currentPage.value++
    if (currentPage.value > totalPage.value) {
      currentPage.value = 1
    }
    updateChart()
  }, 5000);
}
// 屏幕适配【当浏览器的大小发生变化时,来完成屏幕的适配】
const screenAdapter = () => {
  const data = allData.value.slice((currentPage.value - 1) * 5, currentPage.value * 5)
  const titleFontSize = sellerRef.value.offsetWidth / 100 * 3.6
  // 和分辨率大小相关的配置项
  const adapterOption = {
    title: {
      textStyle: {
        fontSize: titleFontSize
      },
    },
    series: [
      {
        type: 'pie',//图表类型
        radius: [titleFontSize * 2.8, titleFontSize * 3.15],//[内圆半径,外圆半径]
        label: {
          fontSize: titleFontSize / 2
        }
      },
      {
        type: 'pie',//图表类型
        radius: [titleFontSize * 2.8, titleFontSize * 3.15],//[内圆半径,外圆半径]
        label: {
          fontSize: titleFontSize / 2
        }
      },
      {
        type: 'pie',//图表类型
        radius: [titleFontSize * 2.8, titleFontSize * 3.15],//[内圆半径,外圆半径]
        label: {
          fontSize: titleFontSize / 2
        }
      },
      {
        type: 'pie',//图表类型
        radius: [titleFontSize * 2.8, titleFontSize * 3.15],//[内圆半径,外圆半径]
        label: {
          fontSize: titleFontSize / 2
        }
      },
      {
        type: 'pie',//图表类型
        radius: [titleFontSize * 2.8, titleFontSize * 3.15],//[内圆半径,外圆半径]
        label: {
          fontSize: titleFontSize / 2
        }
      }
    ]
  }
  chartInstance.value.setOption(adapterOption)
  // 手动调用图标对象的resize方法,才能生效
  chartInstance.value.resize()
}
// 将当前组件中的变量暴露出去,让父组件进行调用
defineExpose({ screenAdapter })
onMounted(() => {
  initChart()
  // getData()
  // 发送数据给服务器,告诉服务器,我现在需要数据
  Socket.send({
    action: 'getData',
    socketType: 'stockData',
    chartName: 'stock',
    value: ''
  })
  // 监听窗口大小变化事件
  window.addEventListener('resize', screenAdapter)
  // 在页面加载完成的时候,主动进行屏幕的适配 
  screenAdapter()
})
onBeforeUnmount(() => {
  clearInterval(timer.value)
  // 在页面加载完成的时候,主动进行屏幕的适配
  window.removeEventListener('resize', screenAdapter)
  Socket.unregisterCallback('stockData')
})
</script>

3.6 商品销量趋势表

<template>
  <div class="com-container">
    <div
      class="title"
      :style="{ fontSize: titleSize + 'px', color: theme.color, background: theme.color === '#000' ? '#fff' : '#222733' }"
    >
      <div
        class="title-con"
        @click="() => isShow = !isShow"
      >
        <span>▎{{ curTitle.text }}</span>
        <span
          class="iconfont"
          :style="{ fontSize: titleSize + 'px' }"
        >&#xe6eb;</span>
      </div>
      <div
        class="select-con"
        v-show="isShow"
        :style="{ marginLeft: ((titleSize / 3) * 2) + 'px' }"
      >
        <div
          v-for="(item, index) in   title"
          :key=index
          class="select-item"
          @click="handleChangeTitle(item)"
        >{{ item.text }}</div>
      </div>
    </div>
    <div
      class="com-chart"
      ref="trendRef"
    ></div>
  </div>
</template>

<script setup>
import { computed, onBeforeUnmount, onMounted, ref, shallowRef, watch, } from 'vue'
import api from '@/utils/url'
import { get } from '@/utils/request'
import * as echarts from 'echarts'
import Socket from '@/utils/socket'
// 引入仓库
import { useChart } from '@/store'
import { getThemeValue } from '@/utils/theme'

const store = useChart()

const isShow = ref(false)
const titleSize = ref()
const trendRef = ref(null)//图表容器
const chartInstance = shallowRef(null)//图表实例
const theme = computed(() => getThemeValue(store.theme))
// 监听主题的切换
watch(() => store.theme, () => {
  // 一旦主题切换了就将原来的图表销毁掉
  chartInstance.value.dispose()
  initChart()// 重新以最新的主题进行初始化图表
  screenAdapter()//完成屏幕适配
  updateChart()//更新图表
})
// 初始化柱状图实例化对象
const initChart = () => {
  chartInstance.value = echarts.init(trendRef.value, store.theme)
  // 对图标初始化的配置
  const initOption = {
    // 坐标轴四周的边距
    grid: {
      top: "35%",
      left: "3%",
      right: "4%",
      bottom: "1%",
      containLabel: true//距离是否包含坐标轴刻度标签
    },
    // x轴配置
    xAxis: {
      type: 'category',
      boundaryGap: false // 是否显示坐标轴两边的空白
    },
    // y轴配置
    yAxis: {
      type: 'value',
    },
    // 提示框配置
    tooltip: {
      trigger: 'axis',//鼠标移入到坐标轴的时候触发提示框
    },
    legend: {
      left: 20,
      top: '15%',
      icon: 'circle'//图例形状
    }
  }
  chartInstance.value.setOption(initOption)
}
const title = computed(() => {
  if (!allData.value) {
    return []
  } else {
    return allData.value.type.filter(item => item.key !== curTitle.value.key)
  }
})
const curTitle = ref({})
// 切换标题
const handleChangeTitle = (item) => {
  curTitle.value = item
  isShow.value = false
  updateChart()//切换标题之后更新数据
}
const allData = ref(null)
// 获取服务器数据
// res: 服务器返回的图表数据
const getData = async (res) => {
  // const res = await get(api.trend)
  // 对数据进行排序,从小到大
  allData.value = res
  curTitle.value = res.type[0]
  updateChart()
}
// 在组件创建完成之后,进行回调函数的注册
Socket.registerCallback('trendData', getData)
// 更新图表
const updateChart = () => {
  const { common, map, commodity, seller, type } = allData.value
  const data = allData.value[curTitle.value.key].data
  const colorArr1 = [
    'rgba(11,168,44,0.5)',
    'rgba(44,110,255,0.5)',
    'rgba(22,242,217,0.5)',
    'rgba(254,33,30,0.5)',
    'rgba(250,105,0,0.5)',
  ]
  const colorArr2 = [
    'rgba(11,168,44,0)',
    'rgba(44,110,255,0)',
    'rgba(22,242,217,0)',
    'rgba(254,33,30,0)',
    'rgba(250,105,0,0)',
  ]
  // 图表配置项
  const option = {
    xAxis: {
      data: common.month//类目轴数据
    },
    yAxis: {
      data: data,//y轴数据
    },
    // 图表类型
    series: data.map((item, index) => {
      // 将stack都设置为一样的值就可以变为折线堆叠图
      return {
        name: item.name,
        type: 'line',
        data: item.data,
        stack: 'map',
        areaStyle: {
          /* 
            线性渐变:
              指明颜色渐变的方向
              指明不同百分比之下颜色的值
              0,0,1,0:表示两个坐标(0,0)(0,1)【从上到下渐变】
          */
          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
            { offset: 0, color: colorArr1[index] },//0%处的颜色
            { offset: 1, color: colorArr2[index] }//100%处的颜色
          ])
        }
      }
    }),
    legend: {
      data: data.map(item => item.name),
    }
  }
  chartInstance.value.setOption(option)
}

// 屏幕适配【当浏览器的大小发生变化时,来完成屏幕的适配】
const screenAdapter = () => {
  const titleFontSize = trendRef.value.offsetWidth / 100 * 3.6
  titleSize.value = titleFontSize
  // 和分辨率大小相关的配置项
  const adapterOption = {
    legend: {
      // 设置图标的大小和文字大小
      itemWidth: titleFontSize,
      itemHeight: titleFontSize,
      textStyle: {
        fontSize: titleFontSize
      },
    }
  }
  chartInstance.value.setOption(adapterOption)
  // 手动调用图标对象的resize方法,才能生效
  chartInstance.value.resize()
}
// 将当前组件中的变量暴露出去,让父组件进行调用
defineExpose({ screenAdapter })
onMounted(() => {
  initChart()
  // getData()
  // 发送数据给服务器,告诉服务器,我现在需要数据
  Socket.send({
    action: 'getData',
    socketType: 'trendData',
    chartName: 'trend',
    value: ''
  })
  // 监听窗口大小变化事件
  window.addEventListener('resize', screenAdapter)
  // 在页面加载完成的时候,主动进行屏幕的适配 
  screenAdapter()
})
onBeforeUnmount(() => {
  // 在页面加载完成的时候,主动进行屏幕的适配
  window.removeEventListener('resize', screenAdapter)
  // 在组件销毁的时候,进行回调函数的注销
  Socket.unregisterCallback('trendData')
})
</script>
<style lang="scss" scoped>
.title {
  position: absolute;
  left: 20px;
  top: 20px;
  z-index: 1;
  color: #fff;
  // background: #222733;

  &-con {
    cursor: pointer; //将鼠标移入时的指针改为小手

    .iconfont {
      margin-left: 10px;
    }
  }
}
</style>

3.7 展示页面

<template>
  <div
    class="screen-container"
    :style="{ backgroundColor: theme.backgroundColor, color: theme.color }"
  >
    <header class="header">
      <div>
        <img :src="headerSrc">
      </div>
      <span class="title">电商平台实时监控系统</span>
      <div class="title-right">
        <img
          :src="themeSrc"
          class="qiehuan"
          @click="handleTheme"
        >
        <span class="datetime">2424-12-21 15:32:00</span>
      </div>
    </header>
    <div class="body">
      <section class="left">
        <div
          class="top"
          :class="{ fullscreen: fullScreenStatus.trend }"
        >
          <!-- 销量趋势图表 -->
          <Trend ref="trendRef" />
          <div
            class="resize"
            @click="handleFullScreen('trend')"
          >
            <span
              class="iconfont"
              :class="fullScreenStatus.trend ? 'icon-compress-alt' : 'icon-expand-alt'"
            ></span>
          </div>
        </div>
        <div
          class="bottom"
          :class="{ fullscreen: fullScreenStatus.seller }"
        >
          <!-- 商家销售金额图表 -->
          <Seller ref="sellerRef" />
          <div
            class="resize"
            @click="handleFullScreen('seller')"
          >
            <span
              class="iconfont "
              :class="fullScreenStatus.trend ? 'icon-compress-alt' : 'icon-expand-alt'"
            ></span>
          </div>
        </div>
      </section>
      <section class="middle">
        <div
          class="top"
          :class="{ fullscreen: fullScreenStatus.map }"
        >
          <!-- 商家分布图表 -->
          <Map ref="mapRef" />
          <div
            class="resize"
            @click="handleFullScreen('map')"
          >
            <span
              class="iconfont"
              :class="fullScreenStatus.trend ? 'icon-compress-alt' : 'icon-expand-alt'"
            ></span>
          </div>
        </div>
        <div
          class="bottom"
          :class="{ fullscreen: fullScreenStatus.rank }"
        >
          <!-- 地区销量排行图表 -->
          <Rank ref="rankRef" />
          <div
            class="resize"
            @click="handleFullScreen('rank')"
          >
            <span
              class="iconfont"
              :class="fullScreenStatus.trend ? 'icon-compress-alt' : 'icon-expand-alt'"
            ></span>
          </div>
        </div>
      </section>
      <section class="right">
        <div
          class="top"
          :class="{ fullscreen: fullScreenStatus.hot }"
        >
          <!-- 热销商品占比图表-->
          <Hot ref="hotRef" />
          <div
            class="resize"
            @click="handleFullScreen('hot')"
          >
            <span
              class="iconfont"
              :class="fullScreenStatus.trend ? 'icon-compress-alt' : 'icon-expand-alt'"
            ></span>
          </div>
        </div>
        <div
          class="bottom"
          :class="{ fullscreen: fullScreenStatus.stock }"
        >
          <!-- 库存销量分析图表 -->
          <Stock ref="stockRef" />
          <div
            class="resize"
            @click="handleFullScreen('stock')"
          >
            <span
              class="iconfont"
              :class="fullScreenStatus.trend ? 'icon-compress-alt' : 'icon-expand-alt'"
            ></span>
          </div>
        </div>
      </section>
    </div>
  </div>
</template>

<script setup>
// 引入所需要的图表组价
import Hot from '@/components/Hot.vue'
import Map from '@/components/Map.vue'
import Rank from '@/components/Rank.vue'
import Seller from '@/components/Seller.vue'
import Stock from '@/components/Stock.vue'
import Trend from '@/components/Trend.vue'
import { reactive, ref } from '@vue/reactivity'
import { computed, nextTick, onMounted, onUnmounted } from '@vue/runtime-core'
import Socket from '@/utils/socket'
// 引入仓库
import { useChart } from '@/store'
import { getThemeValue } from '@/utils/theme'

const theme = computed(() => getThemeValue(store.theme))
const headerSrc = computed(() => '/images/' + theme.value.headerBorderSrc)
const themeSrc = computed(() => '/images/' + theme.value.themeSrc)
const store = useChart()
// 点击切换主题
const handleTheme = () => {
  Socket.send({
    action: 'themeChange',
    socketType: 'themeChange',
    chartName: '',
    value: ''
  })
}
// 接收到服务器返回的切换主题之后的数据
const recvThemeChange = () => {
  store.changeTheme()
}
// 注册切换主题的回调函数
Socket.registerCallback('themeChange', recvThemeChange)
// 接收到服务器返回的全屏数据之后的处理
const revData = (res) => {
  // 取出是哪一个图表需要进行切换
  // 取出需要切换成什么状态
  const { chartName, value } = res
  fullScreenStatus[chartName] = value
  nextTick(() => {
    switch (chartName) {
      case 'trend':
        trendRef.value.screenAdapter()
        break;
      case 'seller':
        sellerRef.value.screenAdapter()
        break;
      case 'map':
        mapRef.value.screenAdapter()
        break;
      case 'rank':
        rankRef.value.screenAdapter()
        break;
      case 'hot':
        hotRef.value.screenAdapter()
        break;
      case 'stock':
        stockRef.value.screenAdapter()
        break;
    }
  })
}
// 注册接收到数据的回调函数
Socket.registerCallback('fullScreen', revData)

//定义每一个图表的全屏状态数据,同一时刻只能有一个处于全屏状态
const fullScreenStatus = reactive({
  trend: false,
  seller: false,
  map: false,
  rank: false,
  hot: false,
  stock: false
})
const trendRef = ref(null)
const sellerRef = ref(null)
const mapRef = ref(null)
const rankRef = ref(null)
const hotRef = ref(null)
const stockRef = ref(null)
// 点击切换全屏状态
const handleFullScreen = (type) => {
  const isfull = fullScreenStatus[type]
  // 将数据发送给服务器
  Socket.send({
    action: 'fullScreen',
    socketType: 'fullScreen',
    chartName: type,
    value: !isfull
  })
}
onUnmounted(() => {
  // 在组件销毁的时候注销注册的回调函数
  Socket.unregisterCallback('fullScreen')
  Socket.unregisterCallback('themeChange')
})
</script>

<style lang="scss" scoped>
.screen-container {
  padding: 0 20px;
  width: 100%;
  height: 100%;
  // background: #161522;
  color: #fff;
  box-sizing: border-box;

  .header {
    position: relative;
    width: 100%;
    height: 64px;
    font-size: 20px;

    &>div {
      img {
        width: 100%;
      }
    }

    .title {
      position: absolute;
      left: 50%;
      top: 50%;
      font-size: 20px;
      transform: translate(-50%, -50%);

      &-right {
        display: flex;
        align-items: center;
        position: absolute;
        right: 0px;
        top: 50%;
        transform: translateY(-80%);

        .qiehuan {
          width: 28px;
          height: 21px;
          cursor: pointer;
        }

        .datetime {
          font-size: 15px;
          margin-left: 10px;
        }
      }
    }
  }

  .body {
    width: 100%;
    height: 100%;
    display: flex;
    margin-top: 10px;

    .top,
    .bottom {
      position: relative;
    }

    .left {
      height: 100%;
      width: 27.6%;

      .top {
        height: 53%;
      }

      .bottom {
        height: 31%;
        margin-top: 25px;
      }
    }

    .middle {
      height: 100%;
      width: 41.5%;
      margin-left: 1.6%;
      margin-right: 1.6%;

      .top {
        width: 100%;
        height: 56%;
      }

      .bottom {
        margin-top: 25px;
        width: 100%;
        height: 28%;
      }
    }

    .right {
      height: 100%;
      width: 27.6%;

      .top {
        height: 46%;
      }

      .bottom {
        height: 38%;
        margin-top: 25px;
      }
    }

    .resize {
      position: absolute;
      right: 20px;
      top: 20px;
      cursor: pointer;
    }
  }

  // 全屏样式
  .fullscreen {
    margin-top: 0 !important;
    position: fixed !important;
    top: 0;
    left: 0;
    right: 0;
    height: 100% !important;
    z-index: 100;
  }
}
</style>

说明:

以上代码只展示了前端6个图表组件以及展示页面的所有代码,axios部分在使用了websocket之后就可以不用了。

如果想要完整的钱后端代码可以从仓库进行下载

zss5527/大屏可视化icon-default.png?t=O83Ahttps://gitee.com/zss5527/large-screen-visualization.git

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

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

相关文章

ECharts关系图-关系图11,附视频讲解与代码下载

引言&#xff1a; 关系图&#xff08;或称网络图、关系网络图&#xff09;在数据可视化中扮演着至关重要的角色。它们通过节点&#xff08;代表实体&#xff0c;如人、物体、概念等&#xff09;和边&#xff08;代表实体之间的关系或连接&#xff09;的形式&#xff0c;直观地…

javaEE--计算机是如何工作的-1

目录 一.计算机的组成: 各组件的功能: 衡量cpu好坏的标准: 二.指令(instruction) 三.操作系统Operating System 四.进程/任务process/tesk 五.进程在系统中如何管理 1.进程在系统中的管理,从两个角度来分类: 2.进程控制块PCB&#xff08;Process Control Block)) 3.P…

目标检测-R-CNN

R-CNN在2014年被提出&#xff0c;算法流程可以概括如下&#xff1a; 候选区域生成&#xff1a;利用选择性搜索(selective search)方法找出图片中可能存在目标的候选区域(region proposal) CNN网络提取特征&#xff1a;对候选区域进行特征提取(可以使用AlexNet、VGG等网络) 目…

Blender 中投影仪的配置与使用

Blender 中投影仪的配置与使用 Blenderdownloadbasic Projectordownloadinstallconfigure 利用Blender中的投影仪搭建一个简单的结构光仿真系统&#xff0c;通过调整被测对象的材质和投影仪位姿以及投影来获得不同的渲染图像。 Blender download 在官网中下载相应安装包&…

MYSQL慢查询日志(开启慢查询配置、explain执行计划SQL优化、各个字段详解、索引失效)

大家好&#xff0c;我是此林。 今天来分享一下MYSQL慢查询日志记录。 目录 1. 定义 2. 开启慢查询 方法一&#xff1a;命令行 方法二&#xff1a;修改配置文件 3. explain性能分析 4. 索引失效 1. 最左前缀法则 2. 对字段做运算、字段类型不匹配 3. 模糊匹配 4. OR…

Leetcode打卡:考场就坐

执行结果&#xff1a;通过 题目&#xff1a; 855 考场就坐 在考场里&#xff0c;有 n 个座位排成一行&#xff0c;编号为 0 到 n - 1。 当学生进入考场后&#xff0c;他必须坐在离最近的人最远的座位上。如果有多个这样的座位&#xff0c;他会坐在编号最小的座位上。(另外&am…

2024.2 ACM Explainability for Large Language Models: A Survey

Explainability for Large Language Models: A Survey | ACM Transactions on Intelligent Systems and Technology 问题 可解释性问题&#xff1a;大语言模型&#xff08;LLMs&#xff09;内部机制不透明&#xff0c;难以理解其决策过程&#xff0c;如在自然语言处理任务中&…

解决“SVN无法上传或下载*.so、*.a等二进制文件“问题

今天&#xff0c;在使用Subversion提交代码到服务器时&#xff0c;发现无法提交*.a、*.so等二进制文件&#xff0c;右击这些文件&#xff0c;发现其属性为ignores。     问题原因&#xff1a;SVN的配置文件里&#xff0c;屏蔽了*.a、*.so文件的上传与下载&#xff0c;并把这些…

层序遍历练习

层次遍历 II 给定一个二叉树&#xff0c;返回其节点值自底向上的层次遍历。 &#xff08;即按从叶子节点所在层到根节点所在的层&#xff0c;逐层从左向右遍历&#xff09; 思路 相对于102.二叉树的层序遍历&#xff0c;就是最后把result数组反转一下就可以了。 C代码&…

京东大数据治理探索与实践 | 京东零售技术实践

01背景和方案 在当今的数据驱动时代&#xff0c;数据作为关键生产要素之一&#xff0c;其在商业活动中的战略价值愈加凸显&#xff0c;京东也不例外。 作为国内领先的电商平台&#xff0c;京东在数据基础设施上的投入极为巨大&#xff0c;涵盖数万台服务器、数 EB 级存储、数百…

【论文阅读笔记】Learning to sample

Learning to sample 前沿引言方法问题声明S-NET匹配ProgressiveNet: sampling as ordering 实验分类检索重建 结论附录 前沿 这是一篇比较经典的基于深度学习的点云下采样方法 核心创新点&#xff1a; 首次提出了一种学习驱动的、任务特定的点云采样方法引入了两种采样网络&…

[AIGC知识] layout理解

前言 要开组会了&#xff0c;随便讲个凑数吧。 参考论文 https://arxiv.org/html/2303.17189? 什么是layout数据&#xff1f; 像下图这样&#xff0c;Layout是每个图片的布局&#xff0c;其中包含一些物体的相应边界框和类别 layout信息如何整合表示并作为条件加入到网络…

【macos java反编译工具Java Decompiler】

mac上能用的反编译工具 https://java-decompiler.github.io/

C#+OpenCv深度学习开发(常用模型汇总)

在使用 OpenCvSharp 结合深度学习进行机器视觉开发时&#xff0c;有许多现成的模型可以使用。以下是一些常用的深度学习模型&#xff0c;适用于不同的机器视觉任务&#xff0c;包括物体检测、图像分类和分割等。 使用示例 在 OpenCvSharp 中加载和使用这些模型的基本示例&…

【生成模型之七】Classifier-free diffusion guidance

论文&#xff1a;classifier-free diffusion guidance 一、Background 分类器引导是一种最近引入的方法&#xff0c;用于在训练后的条件扩散模型中权衡样本丰富度和样本保真度&#xff0c;其思想与其他类型生成模型中的低温采样或截断相同。 分类器引导将扩散模型的分数估计…

【LeetCode每日一题】——415.字符串相加

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时空频度】九【代码实现】十【提交结果】 一【题目类别】 字符串 二【题目难度】 简单 三【题目编号】 415.字符串相加 四【题目描述】 给定两个字符…

Why SAP TM?

最近发现跟 SAP TM 的集成越来越多了&#xff0c;并且发现这模块还挺大&#xff0c;很难一下子理解。TM&#xff08;Transportation Management&#xff09;- 顾名思义就是“运输管理”。起初很难想象为啥 SAP 会浪费大量的时间和精力开发“运输管理”&#xff0c;从而只是为了…

开源鸿蒙 5.0 正式版发布

在2024年的开放原子开发者大会上&#xff0c;开源鸿蒙5.0版本正式发布啦&#xff01;这个版本是一个比较大的升级&#xff0c;性能和功能都上了一个新台阶&#xff0c;让我们一起来看看都有哪些亮点。 首先&#xff0c;开源鸿蒙这个项目&#xff0c;从最初的700万行代码&#x…

直流有刷电机多环控制(PID闭环死区和积分分离)

直流有刷电机多环控制 提高部分-第8讲 直流有刷电机多环控制实现(1)_哔哩哔哩_bilibili PID模型 外环的输出作为内环的输入&#xff0c;外环是最主要控制的效果&#xff0c;主要控制电机的位置。改变位置可以改变速度&#xff0c;改变速度是受电流控制。 实验环境 【 &…

Odrive源码分析(四) 位置爬坡算法

Odrive中自带一个简单的梯形速度爬坡算法&#xff0c;本文分析下这部分代码。 代码如下&#xff1a; #include <cmath> #include "odrive_main.h" #include "utils.hpp"// A sign function where input 0 has positive sign (not 0) float sign_ha…