11wqe1 hace 1 semana
padre
commit
d5d8841d42
Se han modificado 22 ficheros con 3139 adiciones y 3 borrados
  1. 150 0
      src/views/kanBan/components/dataBoardNew/AIApp/chartList/ParetoChart.vue
  2. 118 0
      src/views/kanBan/components/dataBoardNew/AIApp/chartList/appCreate.vue
  3. 141 0
      src/views/kanBan/components/dataBoardNew/AIApp/chartList/appKey.vue
  4. 112 0
      src/views/kanBan/components/dataBoardNew/AIApp/chartList/appPub.vue
  5. 102 0
      src/views/kanBan/components/dataBoardNew/AIApp/chartList/appUse.vue
  6. 72 0
      src/views/kanBan/components/dataBoardNew/AIApp/chartList/conKey.vue
  7. 230 0
      src/views/kanBan/components/dataBoardNew/AIApp/index.vue
  8. 36 0
      src/views/kanBan/components/dataBoardNew/barList.vue
  9. 182 0
      src/views/kanBan/components/dataBoardNew/baseData/chartList/levChart.vue
  10. 180 0
      src/views/kanBan/components/dataBoardNew/baseData/chartList/teahoop.vue
  11. 194 0
      src/views/kanBan/components/dataBoardNew/baseData/chartList/tealiveness.vue
  12. 246 0
      src/views/kanBan/components/dataBoardNew/baseData/chartList/useTimer.vue
  13. 131 0
      src/views/kanBan/components/dataBoardNew/baseData/index.vue
  14. 158 0
      src/views/kanBan/components/dataBoardNew/index.vue
  15. 113 0
      src/views/kanBan/components/dataBoardNew/teaMange/chartList/pie2.vue
  16. 141 0
      src/views/kanBan/components/dataBoardNew/teaMange/chartList/solid.vue
  17. 147 0
      src/views/kanBan/components/dataBoardNew/teaMange/index.vue
  18. 125 0
      src/views/kanBan/components/dataBoardNew/teaing/chartList/BubbleChart.vue
  19. 135 0
      src/views/kanBan/components/dataBoardNew/teaing/chartList/aidedDesign.vue
  20. 179 0
      src/views/kanBan/components/dataBoardNew/teaing/chartList/appUseAss.vue
  21. 240 0
      src/views/kanBan/components/dataBoardNew/teaing/index.vue
  22. 7 3
      src/views/kanBan/index.vue

+ 150 - 0
src/views/kanBan/components/dataBoardNew/AIApp/chartList/ParetoChart.vue

@@ -0,0 +1,150 @@
+<template>
+  <div class="chartsT">
+      应用使用分析(帕累托图)
+      <div ref="chart" style="width:100%;flex: 1;"></div>
+  </div>
+</template>
+
+<script>
+import * as echarts from 'echarts';
+
+export default {
+  name: "ParetoChart",
+  props: {
+    chartData: {
+      type: Array,
+      default: () => []
+    }
+  },
+  data() {
+    return {
+      rawData: [],
+      chart: null
+    }
+  },
+
+  computed: {
+    
+    // 按 form_count 降序排列
+    sortedData() {
+      return this.rawData.slice().sort((a, b) => b.form_count - a.form_count);
+    },
+    xData() {
+      return this.sortedData.map(item => item.name);
+    },
+    barData() {
+      return this.sortedData.map(item => item.form_count);
+    },
+    lineData() {
+      // 计算累计百分比
+      let total = this.barData.reduce((sum, v) => sum + v, 0);
+      let acc = 0;
+      return this.barData.map(v => {
+        acc += v;
+        return +(acc / total * 100).toFixed(2);
+      });
+    }
+  },
+  watch: {
+    chartData:{
+        handler(val) {                
+            if (val.length) {
+                this.rawData = JSON.parse(JSON.stringify(val))
+                this.initChart();
+            }
+        },
+        deep:true,
+        immediate: true  // 组件创建时立即执行
+    }
+  },
+  mounted() {
+    // this.rawData = JSON.parse(JSON.stringify(this.chartData))
+    // this.initChart();
+    window.addEventListener('resize', this.handleResize);
+    
+  },
+  beforeDestroy() {
+    window.removeEventListener('resize', this.handleResize);
+    this.chart && this.chart.dispose();
+  },
+  methods: {
+    initChart() {
+      this.chart = echarts.init(this.$refs.chart);
+      const option ={
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: { type: 'shadow' }
+        },
+        grid: { left: 60, right: 60, bottom: 60, top: 60 },
+        xAxis: [
+          {
+            type: 'category',
+            data: this.xData,
+            axisLabel: {
+              rotate: 30,
+              interval: 0
+            }
+          }
+        ],
+        yAxis: [
+          {
+            type: 'value',
+            // name: '数量',
+            minInterval: 1
+          },
+          {
+            type: 'value',
+            // name: '累计百分比',
+            min: 0,
+            max: 100,
+            axisLabel: { formatter: '{value}%' }
+          }
+        ],
+        series: [
+          {
+            name: '使用数量',
+            type: 'bar',
+            data: this.barData,
+            itemStyle: {
+              color: '#5470c6'
+            }
+          },
+          {
+            name: '累计百分比',
+            type: 'line',
+            yAxisIndex: 1,
+            data: this.lineData,
+            symbol: 'circle',
+            symbolSize: 8,
+            lineStyle: {
+              color: '#91cc75'
+            },
+            itemStyle: {
+              color: '#91cc75'
+            },
+            label: {
+              show: true,
+              formatter: '{c}%',
+              position: 'top'
+            }
+          }
+        ]
+      };
+      this.chart.setOption(option)
+      window.removeEventListener("resize", this.chart.resize);
+    },
+    handleResize() {
+      this.chart && this.chart.resize();
+    },
+  }
+};
+</script>
+
+<style scoped>
+.chartsT{
+  width: 100%;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+}
+</style>

+ 118 - 0
src/views/kanBan/components/dataBoardNew/AIApp/chartList/appCreate.vue

@@ -0,0 +1,118 @@
+<template>
+  <div ref="chart" style="height: 100%;width: 100%;"></div>
+</template>
+
+<script>
+import * as echarts from 'echarts';
+
+export default {
+  props: {
+    // chartData: {
+    //   type: Array, // 数据形式为 [[x1, y1], [x2, y2], ...]
+    //   required: true,
+    // },
+  },
+  mounted() {
+    console.log('chartData',this.chartData);
+    
+    // this.initChart();
+  },
+  methods: {
+    calculateTrendLine(data) {
+      // 计算趋势线的斜率和截距
+      const n = data.length;
+      let sumX = 0,
+        sumY = 0,
+        sumXY = 0,
+        sumX2 = 0;
+
+      data.forEach(([x, y]) => {
+        sumX += x;
+        sumY += y;
+        sumXY += x * y;
+        sumX2 += x * x;
+      });
+
+      const slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
+      const intercept = (sumY - slope * sumX) / n;
+
+      // 根据斜率和截距生成趋势线数据
+      const minX = Math.min(...data.map(([x]) => x));
+      const maxX = Math.max(...data.map(([x]) => x));
+      const trendLine = [
+        [minX, slope * minX + intercept],
+        [maxX, slope * maxX + intercept],
+      ];
+
+      return trendLine;
+    },
+    initChart() {
+      const chart = echarts.init(this.$refs.chart);
+
+      // 计算趋势线数据
+      const trendLine = this.calculateTrendLine(this.chartData);
+
+      const option = {
+        title: {
+          text: '散点图(带趋势线)',
+          left: 'center',
+          textStyle: {
+            fontSize: 16,
+          },
+        },
+        tooltip: {
+          trigger: 'item',
+          formatter: (params) => {
+            if (params.seriesName === '数据点') {
+              return `X: ${params.data[0]}<br>Y: ${params.data[1]}`;
+            }
+            return '';
+          },
+        },
+        xAxis: {
+          type: 'value',
+        //   name: 'X',
+          min: 0,
+        },
+        yAxis: {
+          type: 'value',
+        //   name: 'Y',
+          min: 0,
+        },
+        series: [
+          {
+            name: '数据点',
+            type: 'scatter',
+            data: this.chartData,
+            symbolSize: 10,
+            itemStyle: {
+              color: '#4C76F3',
+            },
+          },
+          {
+            name: '趋势线',
+            type: 'line',
+            data: trendLine,
+            lineStyle: {
+              color: '#F56C6C',
+              type: 'dashed',
+            },
+            symbol: 'none', // 去掉趋势线的点
+          },
+        ],
+      };
+
+      chart.setOption(option);
+      this.chartInstance = chart;
+    },
+  },
+ 
+};
+</script>
+
+<style>
+.chart-container {
+  width: 100%;
+  height: 400px;
+}
+</style>

+ 141 - 0
src/views/kanBan/components/dataBoardNew/AIApp/chartList/appKey.vue

@@ -0,0 +1,141 @@
+<template>
+  <div ref="chart" style="width: 100%; height: 100%;"></div>
+</template>
+
+<script>
+import * as echarts from 'echarts';
+
+export default {
+  name: 'NetworkChart',
+  data() {
+    return {
+      chart: null,
+      staticData: {
+        nodes: [
+          { id: "1", name: "中心节点", symbolSize: 45, category: 0 },
+          { id: "2", name: "技术部", symbolSize: 28, category: 1 },
+          { id: "3", name: "市场部", symbolSize: 32, category: 2 },
+          { id: "4", name: "研发A组", symbolSize: 18, category: 3 },
+          { id: "5", name: "研发B组", symbolSize: 22, category: 3 },
+          { id: "6", name: "华东区", symbolSize: 26, category: 4 },
+          { id: "7", name: "华南区", symbolSize: 24, category: 4 },
+          { id: "8", name: "数据中心", symbolSize: 35, category: 5 },
+          { id: "9", name: "云服务", symbolSize: 30, category: 5 },
+          { id: "10", name: "客户端", symbolSize: 20, category: 6 },
+          { id: "11", name: "移动端", symbolSize: 25, category: 6 },
+          { id: "12", name: "Web端", symbolSize: 22, category: 6 }
+        ],
+        links: [
+          { source: "1", target: "2" },
+          { source: "1", target: "3" },
+          { source: "2", target: "4" },
+          { source: "2", target: "5" },
+          { source: "3", target: "6" },
+          { source: "3", target: "7" },
+          { source: "1", target: "8" },
+          { source: "8", target: "9" },
+          { source: "9", target: "10" },
+          { source: "9", target: "11" },
+          { source: "9", target: "12" },
+          { source: "4", target: "10" },
+          { source: "5", target: "11" },
+          { source: "6", target: "12" },
+          { source: "7", target: "10" }
+        ],
+        categories: [
+          { name: "总部" },
+          { name: "技术部门" },
+          { name: "市场部门" },
+          { name: "研发团队" },
+          { name: "大区" },
+          { name: "基础服务" },
+          { name: "终端平台" }
+        ]
+      }
+    }
+  },
+  mounted() {
+    this.initChart();
+    window.addEventListener('resize', this.handleResize);
+  },
+  methods: {
+    initChart() {
+      this.chart = echarts.init(this.$refs.chart);
+      
+      // 动态计算标签显示条件
+      this.staticData.nodes.forEach(node => {
+        node.label = { 
+          show: node.symbolSize > 20,
+          fontSize: Math.sqrt(node.symbolSize) * 2
+        };
+      });
+
+      this.setChartOption(this.staticData);
+    },
+    setChartOption(graph) {
+      const option = {
+        // title: {
+        //   text: '企业架构关系图',
+        //   subtext: '静态数据示例',
+        //   top: 20,
+        //   left: 'center'
+        // },
+        tooltip: {},
+        legend: {
+          data: graph.categories.map(a => a.name),
+          // orient: 'vertical',
+          right: 10,
+          top: 10
+        },
+        animationDuration: 1500,
+        series: [{
+          type: 'graph',
+          layout: 'force',
+          force: {
+            repulsion: 200,
+            edgeLength: 100
+          },
+          data: graph.nodes,
+          links: graph.links,
+          categories: graph.categories,
+          roam: true,
+          label: {
+            position: 'right',
+            formatter: '{b}',
+            color: '#666'
+          },
+          lineStyle: {
+            color: 'source',
+            curveness: 0.1
+          },
+          emphasis: {
+            focus: 'adjacency',
+            label: {
+              show: true,
+              fontWeight: 'bold'
+            }
+          },
+          // itemStyle: {
+          //   color: (params) => {
+          //     const colors = [
+          //       '#5470c6', '#91cc75', '#fac858', 
+          //       '#ee6666', '#73c0de', '#3ba272', 
+          //       '#fc8452'
+          //     ];
+          //     return colors[params.data.category % colors.length];
+          //   }
+          // }
+        }]
+      };
+      this.chart.setOption(option);
+    },
+    handleResize() {
+      this.chart && this.chart.resize();
+    }
+  },
+  beforeDestroy() {
+    window.removeEventListener('resize', this.handleResize);
+    this.chart.dispose();
+  }
+};
+</script>

+ 112 - 0
src/views/kanBan/components/dataBoardNew/AIApp/chartList/appPub.vue

@@ -0,0 +1,112 @@
+<template>
+  <div class="chartsT">
+      应用发布分类
+      <div ref="chartDom" style="width:100%;height:100%"></div>
+  </div>
+</template>
+
+<script>
+import * as echarts from "echarts";
+
+export default {
+  name: "DonutChart",
+ props: {
+    chartData: {
+      type: Array,
+      default: () => []
+    }
+  },
+  data() {
+    return {
+      chart: null,
+      allNum:'',
+    }
+  },
+  watch: {
+     chartData:{
+          handler(val) {                
+              if (val.length) {
+                  this.initChart();
+              }
+          },
+          deep:true,
+          immediate: true  // 组件创建时立即执行
+      }
+  },
+  beforeDestroy() {
+    window.removeEventListener('resize', this.handleResize);
+    this.chart && this.chart.dispose();
+  },
+  mounted() {
+    // this.initChart();
+    window.addEventListener('resize', this.handleResize);
+  },
+  methods: {
+    handleResize() {
+      this.chart && this.chart.resize();
+    },
+    initChart() {
+      this.chart = echarts.init(this.$refs.chartDom);
+      this.updateChart();
+    },
+    updateChart() {
+     this.allNum = this.chartData.reduce((sum, val) => sum + val.form_count, 0);
+      const option = {
+        tooltip: {
+          trigger: 'item',
+          formatter: '{a} <br/>{b}: {c} ({d}%)'
+        },
+        legend: {
+          orient: 'vertical',
+          right:'right',
+          data: this.chartData.map(item => item.name || '未命名')
+        },
+        series: [
+          {
+            name: '数量占比',
+            type: 'pie',
+            radius: ['30%', '70%'], // 创建环形图
+            avoidLabelOverlap: false,
+            label: {
+              show: false,
+            },
+            labelLine: {
+              show: false
+            },
+            data: this.chartData.map(item => ({
+              value: item.form_count,
+              name: item.name || '未命名'
+            }))
+          }
+        ],
+        graphic: {
+          type: 'text',
+          left: 'center',
+          top: 'middle',
+          style: {
+            text: `总计\n${this.allNum}个`,
+            fill: '#000',
+            font: '20px Arial',
+            textAlign: 'center',
+            transform: 'translate(-50%,-50%)'
+          },
+          silent: true
+        }
+        
+      };
+      this.chart.setOption(option);
+      window.removeEventListener('resize', this.chart.resize);
+
+    },
+    
+  },
+  
+};
+</script>
+
+<style>
+.chartsT{
+  width: 100%;
+  height: 100%;
+}
+</style>

+ 102 - 0
src/views/kanBan/components/dataBoardNew/AIApp/chartList/appUse.vue

@@ -0,0 +1,102 @@
+
+<template>
+  <div ref="chartDom" style="width:100%;height:100%"></div>
+</template>
+
+<script>
+import * as echarts from 'echarts';
+export default {
+  props: {
+    chartData: {
+      type: Array,
+      default: () => []
+    }
+  },
+  data() {
+    return {
+      chart: null,
+      allNum:'',
+    }
+  },
+  computed:{
+    numL(){
+      return this.chartData.reduce((sum, val) => sum + val.form_count, 0);
+    }
+  },
+  watch: {
+     chartData:{
+            handler(val) { 
+              console.log('chartData',val);           
+                if (val.length) {
+                    this.initChart()
+                }
+            },
+            deep:true,
+            immediate: true  // 组件创建时立即执行
+        }
+  },
+  mounted(){
+      this.initChart()
+  },
+  beforeDestroy() {
+    window.removeEventListener('resize', this.handleResize);
+    this.chart.dispose();
+  },
+  methods: {
+    initChart() {
+      this.chart = echarts.init(this.$refs.chartDom);
+      this.updateChart();
+    },
+    updateChart() {
+      const option = {
+        tooltip: {
+          trigger: 'item',
+          formatter: '{a} <br/>{b}: {c} ({d}%)'
+        },
+        legend: {
+          orient: 'vertical',
+          right:'right',
+          data: this.chartData.map(item => item.name || '未命名')
+        },
+        series: [
+          {
+            name: '数量占比',
+            type: 'pie',
+            radius: ['30%', '70%'], // 创建环形图
+            avoidLabelOverlap: false,
+            label: {
+              show: false,
+            },
+            labelLine: {
+              show: false
+            },
+            data: this.chartData.map(item => ({
+              value: item.form_count,
+              name: item.name || '未命名'
+            }))
+          }
+        ],
+        graphic: {
+          type: 'text',
+          left: 'center',
+          top: 'middle',
+          style: {
+            text: `总计\n${this.numL}次`,
+            fill: '#000',
+            font: '20px Arial',
+            textAlign: 'center',
+            transform: 'translate(-50%,-50%)'
+          },
+          silent: true
+        }
+      };
+      this.chart.setOption(option);
+      window.addEventListener('resize', this.chart.resize);
+
+    },
+    handleResize() {
+      this.chart && this.chart.resize();
+    },
+  }
+}
+</script>

+ 72 - 0
src/views/kanBan/components/dataBoardNew/AIApp/chartList/conKey.vue

@@ -0,0 +1,72 @@
+
+<template>
+  <div ref="chartContainer" style="width: 100%; height: 100%;"></div>
+</template>
+
+<script>
+import * as echarts from 'echarts';
+
+export default {
+  name: 'SankeyChart',
+  data() {
+    return {
+      chart: null,
+      chartOption: {
+        series: {
+          type: 'sankey',
+          layout: 'none',
+          emphasis: {
+            focus: 'adjacency'
+          },
+          data: [
+            { name: 'a' },
+            { name: 'b' },
+            { name: 'a1' },
+            { name: 'a2' },
+            { name: 'b1' },
+            { name: 'c' }
+          ],
+          links: [
+            { source: 'a', target: 'a1', value: 5 },
+            { source: 'a', target: 'a2', value: 3 },
+            { source: 'b', target: 'b1', value: 8 },
+            { source: 'a', target: 'b1', value: 3 },
+            { source: 'b1', target: 'a1', value: 1 },
+            { source: 'b1', target: 'c', value: 2 }
+          ],
+          itemStyle: {
+            color: '#4b94d1',
+            borderColor: '#2a5d8a'
+          },
+          lineStyle: {
+            color: 'source',
+            curveness: 0.5
+          }
+        }
+      }
+    }
+  },
+  mounted() {
+    this.initChart();
+    window.addEventListener('resize', this.handleResize);
+  },
+  methods: {
+    initChart() {
+      this.chart = echarts.init(this.$refs.chartContainer);
+      this.chart.setOption(this.chartOption);
+      
+      // 添加点击事件监听
+      this.chart.on('click', params => {
+        console.log('节点点击:', params.name);
+      });
+    },
+    handleResize() {
+      this.chart && this.chart.resize();
+    }
+  },
+  beforeDestroy() {
+    window.removeEventListener('resize', this.handleResize);
+    this.chart && this.chart.dispose();
+  }
+};
+</script>

+ 230 - 0
src/views/kanBan/components/dataBoardNew/AIApp/index.vue

@@ -0,0 +1,230 @@
+<template>
+  <div class="body1" v-loading="isLoading">
+    <barList :dataExponentList="dataExponentList"></barList>
+    <!-- 第一行 -->
+    <div class="AIAppOne">
+      <div class="AIAppOneCon">
+        热门应用Top5
+        <div class="hotCon">
+          <div v-for="(i,index) in alldata[0]" :key="index" class="hotConA">
+            <div class="hotConAA">
+              <span>{{ index + 1 }}</span>
+              <div class="hotConAATxt">{{ i.name }}</div>
+            </div>
+            <div style="color: #3072D8;">{{ i.form_count }}次</div>
+          </div>
+        </div>
+      </div>
+      <div class="AIAppOneCon">
+        创作者Top5
+        <div class="hotCon">
+          <div v-for="(i,index) in alldata[1]" :key="index" class="hotConA">
+            <div class="hotConAA">
+              <span>{{ index + 1 }}</span>
+              <div class="hotConAATxt">{{ i.username }}</div>
+            </div>
+            <div style="color: #3072D8;">{{ i.form_count }}个</div>
+          </div>
+        </div>
+      </div>
+      <div class="AIAppOneCon">
+        应用使用分类
+        <appUse :chartData="alldata[2]"></appUse>
+      </div>
+    </div>
+
+    <div class="AIAppTwo">
+      <div>
+        <appPub :chartData="alldata[3]"></appPub>
+      </div>
+      <div>
+        <ParetoChart :chartData="alldata[4]"></ParetoChart>
+      </div>
+    </div>
+
+    <!-- <div class="AIAppTwo">
+      <div>
+        应用创建分析
+        <appCreate ></appCreate>
+      </div>
+      <div>
+        应用索引网络
+        <appKey></appKey> 
+      </div>
+    </div> -->
+  </div>
+</template>
+
+<script>
+import appUse from "./chartList/appUse.vue";
+import appPub from "./chartList/appPub.vue";
+import ParetoChart from "./chartList/ParetoChart.vue";
+import barList from '../barList.vue';
+import { API_CONFIG } from "@/common/apiConfig";
+
+export default {
+  name:'AIappComponent',
+  components: {
+    appUse,
+    appPub,
+    ParetoChart,
+    barList
+  },
+  props: {
+    oid: {
+      type: String,
+    },
+    org: {
+      type: String,
+    },
+    userid:{
+      type: String,
+    },
+  },
+  data() {
+    return {
+      dataExponentList:[],
+      alldata:[],
+      isLoading: false,
+    };
+  },
+  watch:{
+        oid(newL){
+            console.log(newL);
+
+            this.getAIData()
+        }
+    },
+   methods: {
+        async getAIData() {
+          this.isLoading = true;
+          let params = [
+            {
+              functionName: API_CONFIG.getKanbanAIdata.functionName,
+              suserid:this.userid,
+              oid: this.oid,
+              org: '',
+              exportType:"",
+            },
+          ];
+          // let cot = await this.getAppStoreControl()
+          
+          this.$ajax
+            .post(API_CONFIG.baseUrl, params)
+            .then((res) => {
+                let top = [
+                  { label: 'AI应用数量', value: 0 },
+                  { label: '今日用户数量', value: 0 },
+                  { label: '知识库数量', value: 0 },
+                  { label: '文件数量', value: 0 },
+                ]
+
+                top[0].value = res.data[0][0].num
+                top[1].value = res.data[1][0].num
+                top[2].value = res.data[2][0].num
+                top[3].value = res.data[3][0].num
+              
+                this.dataExponentList = top
+
+                this.alldata = res.data.slice(4)
+                this.isLoading = false;
+
+            })
+            .catch((err) => {
+                this.isLoading = false;
+                console.error(err);
+            });
+        },
+    },
+    mounted() {
+      console.log(666);
+      
+        this.getAIData()
+    },
+};
+</script>
+
+<style scoped>
+.body1 {
+  display: flex;
+  flex-direction: column;
+  gap: 20px;
+  width: 100%;
+  height: 100%;
+  box-sizing: border-box;
+  overflow: hidden;
+  position: relative;
+  padding-bottom: 30px;
+}
+.AIAppOne{
+  width: 100%;
+  display: grid;
+  grid-template-columns: repeat(3, 1fr);  /* 3列网格 */
+  height: 400px;
+  gap: 20px;  /* 间距 */
+}
+.AIAppTwo{
+  display: flex;
+  width: 100%;
+  height: 400px;
+  gap: 20px;  /* 间距 */
+}
+.AIAppTwo div{
+  flex: 1;
+  background: #fff;
+  border-radius: 10px;
+  padding: 10px;
+  box-sizing: border-box;
+}
+
+.AIAppOneCon{
+  padding: 20px;
+  border-radius: 10px;
+  box-sizing: border-box;
+  background: #fff;
+  display: flex;
+  flex-direction: column;
+}
+
+.hotCon{
+  display: flex;
+  flex-direction: column;
+  background: #F8FAFC;
+  padding: 20px 15px;
+  flex: 1;
+  box-sizing: border-box;
+  border-radius: 10px;
+  margin-top: 10px;
+  gap: 5px;
+}
+.hotConA{
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 8px 0;
+  box-sizing: border-box;
+  border-bottom:1px #e7e7e7 solid;
+}
+.hotConAA{
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  flex: 1;
+}
+.hotConAATxt{
+  -webkit-line-clamp: 1;
+  display: -webkit-box;
+  -webkit-box-orient: vertical;
+  overflow: hidden;
+  text-overflow: ellipsis; 
+  width: 100%;
+}
+.hotConAA > span{
+  padding: 0px 5px;
+  color: #fff;
+  margin-right: 5px;
+  box-sizing: border-box;
+  background: #0358D2;
+  border-radius: 3px;
+}
+</style>

+ 36 - 0
src/views/kanBan/components/dataBoardNew/barList.vue

@@ -0,0 +1,36 @@
+<template>
+    <div class="barL">
+        <div class="con" v-for="(i,index) in dataExponentList" :key="index">
+             <div style="color: #0358D2;font-size: 30px;font-weight: 600;">{{ i.value }}</div>
+             <div style="color: #878786;font-size: 14px;">{{ i.label }}</div>
+        </div>
+    </div>
+</template>
+
+<script>
+    export default {
+        props:['dataExponentList'],
+        data(){
+            return{
+
+            }
+        }
+    }
+</script>
+
+<style scoped>
+.barL{
+    grid-template-columns: repeat(4, 1fr);
+    display: grid;
+    background: #fff;
+    border-radius: 10px;
+    height: 100px;
+}
+.con{
+    display: flex;
+    align-items: center;
+    flex-direction: column;
+    justify-content: center;
+    gap: 10px;
+}
+</style>

+ 182 - 0
src/views/kanBan/components/dataBoardNew/baseData/chartList/levChart.vue

@@ -0,0 +1,182 @@
+<template>
+  <div  style="display: flex;flex-direction: column;">
+    <div class="top">
+      <div>活跃用户分层</div>
+      <div class="timeL">
+        <div @click="cut(0)" :class="cutNum == 0 ? 'hov' : ''">日</div>
+        <div @click="cut(1)" :class="cutNum == 1 ? 'hov' : ''">周</div>
+        <div @click="cut(2)" :class="cutNum == 2 ? 'hov' : ''">月</div>
+      </div>
+    </div>
+    <div ref="chartContainer" style="width: 100%; flex: 1;"></div>
+  </div>
+</template>
+
+<script>
+import * as echarts from 'echarts';
+
+export default {
+  props: ['alldata'],
+
+  data() {
+    return {
+        chart: null,
+        pageData: [],
+        cutNum: 0,
+        chartData: []
+    };
+  },
+  // mounted() {
+  //   this.initChart();
+  //   window.addEventListener('resize', this.handleResize);
+  // },
+  watch: {
+     alldata:{
+            handler(val) {                
+                if (val.length) {
+                    this.startCom(val)
+                }
+            },
+            deep:true,
+            immediate: true  // 组件创建时立即执行
+        }
+  },
+  beforeDestroy() {
+    window.removeEventListener('resize', this.handleResize)
+    this.chart && this.chart.dispose();
+  },
+  methods: {
+    startCom(val) {
+        let data = JSON.parse(JSON.stringify(val));
+        this.pageData.push(data[3]); // 得出每日数据
+        this.pageData.push(data[4]);
+        this.pageData.push(data[5]);
+        // console.log('this.pageData', this.pageData);
+        this.chartData = data[3];
+        this.initChart();
+    },
+    handleResize() {
+      this.chart && this.chart.resize();
+    },
+    cut(val) {
+    //   console.log('val', val);
+      this.cutNum = val;
+
+      if (this.pageData[val]) {
+        this.chartData = this.pageData[val];
+
+        // 清空图表
+        this.chart.clear();
+
+        // 使用 nextTick 确保在设置选项之前 DOM 已更新
+        this.$nextTick(() => {
+          this.updateChart(); // 调用 updateChart 设置新选项
+        });
+      } else {
+        console.error('提供的值无效,未找到对应数据:', val);
+      }
+    },
+    initChart() {
+      this.chart = echarts.init(this.$refs.chartContainer);
+      this.updateChart();
+    },
+    updateChart() {
+      const total = this.chartData[0].high + this.chartData[0].mid + this.chartData[0].low;
+
+      const data = [
+        { value: this.chartData[0].high, name: '高频' },
+        { value: this.chartData[0].mid, name: '中频' },
+        { value: this.chartData[0].low, name: '低频' },
+      ];
+
+      const option = {
+        tooltip: {
+          trigger: 'item',
+          formatter: '{a} <br/>{b}: {c} ({d}%)'
+        },
+        legend: {
+          orient: 'vertical',
+          right: '5%',
+          top:'10%',
+          formatter: (name) => {
+            const dataInfo = {
+              '高频': `高频 ${((this.chartData[0].high / total) * 100).toFixed(2)}% (${this.chartData[0].high}人)`,
+              '中频': `中频 ${((this.chartData[0].mid / total) * 100).toFixed(2)}% (${this.chartData[0].mid}人)`,
+              '低频': `低频 ${((this.chartData[0].low / total) * 100).toFixed(2)}% (${this.chartData[0].low}人)`,
+            };
+            return dataInfo[name] || '';
+          }
+        },
+        series: [
+          {
+            name: '用户数量',
+            type: 'pie',
+            radius: ['30%', '60%'], // 创建环形图
+            data: data,
+            emphasis: {
+              itemStyle: {
+                shadowBlur: 10,
+                shadowOffsetX: 0,
+                shadowColor: 'rgba(0, 0, 0, 0.5)'
+              }
+            },
+            itemStyle: {
+              borderColor: '#fff',
+              borderWidth: 2
+            }
+          }
+        ],
+        graphic: {
+          type: 'text',
+          left: 'center',
+          top: 'middle',
+          style: {
+            text: `总计\n ${total}人`,
+            fill: '#000',
+            font: '20px Arial',
+            textAlign: 'center',
+            transform: 'translate(-50%,-50%)'
+          },
+          silent: true
+        }
+      };
+
+      this.chart.setOption(option);
+      window.addEventListener('resize', this.chart.resize);
+    }
+  }
+}
+</script>
+
+<style scoped>
+.top {
+  width: 100%;
+  display: flex;
+  justify-content: space-between;
+  padding: 10px 20px;
+  box-sizing: border-box;
+  align-items: center;
+  
+}
+.timeL {
+  display: flex;
+  justify-content: space-between;
+  gap: 10px;
+  height: 25px;
+}
+.timeL div {
+  width: 30px;
+  text-align: center;
+  box-sizing: border-box;
+  background: #F5F7FA;
+  border-radius: 5px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+}
+.hov {
+  background: #0358D2 !important;
+  color: #fff;
+}
+</style>

+ 180 - 0
src/views/kanBan/components/dataBoardNew/baseData/chartList/teahoop.vue

@@ -0,0 +1,180 @@
+<template>
+  <div style="display: flex;flex-direction: column;">
+    <div class="top">
+      <div>活跃度增长环比</div>
+      <div class="timeL">
+        <div @click="cut(0)" :class="cutNum == 0 ? 'hov' : ''">日</div>
+        <div @click="cut(1)" :class="cutNum == 1 ? 'hov' : ''">周</div>
+        <div @click="cut(2)" :class="cutNum == 2 ? 'hov' : ''">月</div>
+      </div>
+    </div>
+    <div ref="chartContainer" style="width: 100%; flex: 1;"></div>
+  </div>
+</template>
+
+<script>
+import * as echarts from 'echarts';
+
+export default {
+  name: 'TeaHoopChart',
+  props: ['alldata'],
+  data() {
+    return {
+      chart: null,
+      pageData: [],
+      cutNum: 0,
+      chartData: []
+    };
+  },
+  watch: {
+    alldata: {
+      handler(val) {
+        // console.log('val', val);
+        if (val.length) {
+          this.startCom(val);
+        }
+      },
+      deep: true,
+      immediate: true
+    }
+  },
+  mounted() {
+    this.initChart();
+    window.addEventListener('resize', this.handleResize);
+  },
+  beforeDestroy() {
+    window.removeEventListener('resize', this.handleResize);
+    this.chart && this.chart.dispose();
+  },
+  methods: {
+    cut(val) {
+    //   console.log('val', val);
+      this.cutNum = val;
+
+      if (this.pageData[val]) {
+        this.chartData = this.pageData[val];
+
+        // 清空图表
+        this.chart.clear();
+
+        // 使用 nextTick 确保在设置选项之前 DOM 已更新
+        this.$nextTick(() => {
+          this.updateChart(); // 调用 updateChart 设置新选项
+        });
+      } else {
+        console.error('提供的值无效,未找到对应数据:', val);
+      }
+    },
+    startCom(val) {
+      let data = JSON.parse(JSON.stringify(val));
+      this.pageData.push(data[0]); // 得出每日数据
+      this.pageData.push(data[1]);
+      this.pageData.push(data[2]);
+    //   console.log('this.pageData', this.pageData);
+      this.chartData = data[0];
+      this.initChart();
+    },
+    initChart() {
+      this.chart = echarts.init(this.$refs.chartContainer);
+      this.updateChart();
+    },
+    updateChart() {
+      const xData = this.chartData.map(item => item.lab).reverse();
+      const yData = this.chartData.map(item => item.num).reverse();
+
+      // 计算环比增长率
+      const growthRates = this.calculateGrowthRates(yData);
+
+      const option = {
+        tooltip: {
+          trigger: 'axis',
+          formatter: params => {
+            return `日期: ${params[0].name}${this.cutNum == 0 ? '日' : this.cutNum == 1 ? '周' : '月'}<br/>环比增长率: ${Math.round(params[0].value)}%`;
+          }
+        },
+        xAxis: {
+          type: 'category',
+          data: xData,
+          axisLabel: {
+            color: '#666',
+            formatter: value => `${value}${this.cutNum == 0 ? '日' : this.cutNum == 1 ? '周' : '月'}`
+          },
+          axisLine: {
+            lineStyle: { color: '#6FC6F3', width: 2 }
+          }
+        },
+        yAxis: {
+          type: 'value',
+          min: -15, // 设置y轴的最小值
+          max: 15, // 设置y轴的最大值
+          axisLabel: {
+            formatter: '{value}%',
+            color: '#666'
+          },
+          axisLine: { show: true },
+          splitLine: { show: true }
+        },
+        series: [{
+          name: '环比增长率',
+          type: 'bar', // 使用柱状图
+          data: growthRates,
+          itemStyle: {
+            color: (params) => {
+              return params.value >= 0 ? '#5470c6' : '#ef5350'; // 正值为蓝色,负值为红色
+            }
+          },
+        }],
+        label: {
+            show: true, // 显示标签
+            position: 'top', // 标签位置在柱子上方
+             formatter: function(params) {
+                return `${Math.round(params.value)}%`;  // 强制显示整数
+            }
+        },
+      };
+
+      this.chart.setOption(option);
+      window.addEventListener('resize', this.chart.resize);
+    },
+    calculateGrowthRates(values) {
+      return values.map((value, index) => {
+        if (index === 0) return 0; // 第一个值没有增长率
+        return ((value - values[index - 1]) / values[index - 1]) * 100;
+      });
+    },
+    handleResize() {
+      this.chart && this.chart.resize();
+    }
+  }
+}
+</script>
+
+<style scoped>
+.top {
+  width: 100%;
+  display: flex;
+  justify-content: space-between;
+  padding: 10px 20px;
+  box-sizing: border-box;
+  align-items: center;
+}
+.timeL {
+  display: flex;
+  justify-content: space-between;
+  gap: 10px;
+}
+.timeL div {
+  height: 25px;
+  width: 30px;
+  text-align: center;
+  line-height: 25px;
+  box-sizing: border-box;
+  background: #F5F7FA;
+  border-radius: 5px;
+  cursor: pointer;
+}
+.hov {
+  background: #0358D2 !important;
+  color: #fff;
+}
+</style>

+ 194 - 0
src/views/kanBan/components/dataBoardNew/baseData/chartList/tealiveness.vue

@@ -0,0 +1,194 @@
+
+<template>
+  <div style="display: flex;flex-direction: column;">
+    <div class="top">
+        <div>教师活跃度</div>
+        <div class="timeL">
+            <div @click="cut(0)" :class="cutNum ==0 ? 'hov' :''">日</div>
+            <div @click="cut(1)" :class="cutNum ==1 ? 'hov' :''">周</div>
+            <div @click="cut(2)" :class="cutNum ==2 ? 'hov' :''">月</div>
+        </div>
+    </div>
+    <div ref="chartContainer" style="width: 100%; flex: 1;"></div>
+  </div>
+</template>
+
+<script>
+import * as echarts from 'echarts';
+
+export default {
+  name:'TeaLivenessChart',
+
+props:['alldata'],
+  data() {
+    return {
+        chart: null,
+        pageData:[],
+        cutNum:0,
+        chartData:[]
+    }
+  },
+    watch:{
+        alldata:{
+            handler(val) {
+                // console.log('val',val);
+                
+                if (val.length) {
+                    this.startCom(val)
+                }
+            },
+            deep:true,
+            immediate: true  // 组件创建时立即执行
+        }
+    },
+  mounted() {
+    this.initChart()
+    window.addEventListener('resize', this.handleResize)
+  },
+  beforeDestroy() {
+    window.removeEventListener('resize', this.handleResize)
+    this.chart && this.chart.dispose()
+  },
+  methods: {
+    cut(val){
+        // console.log('val',val);
+        this.cutNum = val
+
+        if (this.pageData[val]) {
+          this.chartData = this.pageData[val];
+          
+            // 清空图表
+            this.chart.clear();
+            
+            // 使用 nextTick 确保在设置选项之前 DOM 已更新
+            this.$nextTick(() => {
+                this.updateChart(); // 调用 updateChart 设置新选项
+            });
+        } else {
+            console.error('提供的值无效,未找到对应数据:', val);
+        }
+    },
+    startCom(val){
+            let data = JSON.parse(JSON.stringify(val))
+            this.pageData.push(data[0])   //得出每日数据
+            this.pageData.push(data[1])
+            this.pageData.push(data[2])
+            // console.log('this.pageData',this.pageData);
+            this.chartData = data[0]
+            this.initChart()
+    },
+    
+    initChart() {
+      this.chart = echarts.init(this.$refs.chartContainer)
+      this.updateChart()
+    },
+    updateChart() {
+      const xData = this.chartData.map(item => item.lab).reverse();
+      const yData = this.chartData.map(item => item.num).reverse();
+
+      const option = {
+        tooltip: {
+          trigger: 'axis',
+          formatter: params => {
+            return `日期: ${params[0].name}<br/>数值: ${params[0].value}`;
+          }
+        },
+        xAxis: {
+          type: 'category',
+          data: xData,
+          axisLabel: {
+            color: '#666',
+            formatter: value => `${value}${this.cutNum == 0 ? '日' : this.cutNum == 1 ? '周' :'月'}`
+          },
+          axisLine: {
+            lineStyle: { color: '#6FC6F3', width: 2 }
+          }
+        },
+        yAxis: {
+          type: 'value',
+           minInterval: 1,  // 关键配置
+          axisLabel: {
+            formatter: '{value}人',
+            color: '#666'
+          },
+          axisLine: { show: true },
+          splitLine: { show: true }
+        },
+        series: [{
+          name: '数据量',
+          type: 'line',
+          data: yData,
+          itemStyle: { 
+            color: '#5470c6',
+            borderWidth: 2
+          },
+          lineStyle: { 
+            width: 3,
+            shadowColor: 'rgba(0, 0, 0, 0.3)',
+            shadowBlur: 10,
+            shadowOffsetY: 5
+          },
+          symbol: 'circle',
+          symbolSize: 8,
+          smooth: true
+        }]
+      };
+
+      this.chart.setOption(option);
+      window.addEventListener('resize', this.chart.resize);
+    },
+    handleResize() {
+      this.chart && this.chart.resize()
+    },
+    completeMonthData(data) {
+        console.log('data',data);
+        
+        const currentDate = new Date();
+        const year = currentDate.getFullYear();
+        const month = currentDate.getMonth() + 1;
+        const daysInMonth = new Date(year, month, 0).getDate();
+        
+        const result = [];
+        const existingDays = new Set(data.map(item => item.lab));
+        
+        for (let day = 1; day <= daysInMonth; day++) {
+            const dayStr = day.toString().padStart(2, '0');
+            if (existingDays.has(dayStr)) {
+            result.push(data.find(item => item.lab === dayStr));
+            } else {
+            result.push({ lab: dayStr, num: 0 });
+            }
+        }
+        
+        return result.sort((a, b) => a.lab.localeCompare(b.lab));
+    },
+  }
+}
+</script>
+
+<style scoped>
+.top{
+    width: 100%;
+    display: flex;
+    justify-content: space-between;
+    padding: 10px 20px;
+    box-sizing: border-box;
+    align-items: center;
+}
+.timeL{
+   display: flex;justify-content: space-between;gap: 10px;
+}
+.timeL div{
+    height: 25px;
+    width: 30px;
+    text-align: center;
+    line-height: 25px;
+    box-sizing: border-box;
+    background: #F5F7FA;
+    border-radius: 5px;
+    cursor: pointer;
+}
+.hov{
+  background: #0358D2 !important;color: #fff;
+}
+</style>

+ 246 - 0
src/views/kanBan/components/dataBoardNew/baseData/chartList/useTimer.vue

@@ -0,0 +1,246 @@
+<template>
+  <div  style="display: flex;flex-direction: column;">
+    <div class="top">
+        <div>使用时长</div>
+        <div class="timeL">
+            <div @click="cut(0)" :class="cutNum ==0 ? 'hov' :''">日</div>
+            <div @click="cut(1)" :class="cutNum ==1 ? 'hov' :''">周</div>
+            <div @click="cut(2)" :class="cutNum ==2 ? 'hov' :''">月</div>
+        </div>
+    </div>
+    <div ref="chartContainer" style="width: 100%; flex: 1;"></div>
+  </div>
+</template>
+
+<script>
+import * as echarts from 'echarts';
+
+export default {
+  props:['alldata'],
+
+  data() {
+    return {
+        chart: null,
+        pageData:[[],[],[]],
+        cutNum:0,
+        chartData:[],
+        categories:[],
+        day:[],
+        week:[],
+        month:[]
+
+    };
+  },
+
+  watch:{
+        alldata:{
+            handler(val) {                
+                if (val.length) {
+                    this.startCom(val)
+                }
+            },
+            deep:true,
+            immediate: true  // 组件创建时立即执行
+        }
+    },
+  // mounted() {
+    // this.initChart();
+    // window.addEventListener('resize', this.handleResize)
+  // },
+  methods: {
+    startCom(val){
+        let data = JSON.parse(JSON.stringify(val))
+        // x轴数据
+        this.categories = data[0].map(e=> e.name)
+
+        data[0].forEach(e => {
+            e.chlid = []
+        });
+        
+        // 得出x轴数据
+        // this.pageData = [data[0],data[0],data[0]]
+
+
+        // 日
+        this.day = data[1]
+
+        data[0].forEach(i=>{
+            // 数据分类
+            let kol = []
+            this.day.forEach(e => {
+                if (e.cclassid.includes(i.id)) {                    
+                    kol.push(e)
+                }
+            })
+            // log
+            this.pageData[0].push(this.processBoxplotData(kol))
+        });
+
+        //周
+        this.week = this.sumMinutesByUser(data[2])
+        data[0].forEach(i=>{
+            // 数据分类
+            let kol = []
+            this.week.forEach(e => {
+                if (e.cclassid.includes(i.id)) {                    
+                    kol.push(e)
+                }
+            })
+            this.pageData[1].push(this.processBoxplotData(kol))
+        });
+
+        // 月
+        this.month = this.sumMinutesByUser(data[3])
+        data[0].forEach(i=>{
+            // 数据分类
+            let kol = []
+            this.month.forEach(e => {
+                if (e.cclassid.includes(i.id)) {                    
+                   kol.push(e)
+                }
+            })
+            this.pageData[2].push(this.processBoxplotData(kol))
+        });
+
+        // console.log('this.pageData',this.pageData);
+        
+        this.chartData = this.pageData[0]
+
+
+        this.initChart()
+    },
+    cut(val){
+        // console.log('val',val);
+        this.cutNum = val
+
+        if (this.pageData[val]) {
+          this.chartData = this.pageData[val];
+          
+            // 清空图表
+            this.chart.clear();
+            
+            // 使用 nextTick 确保在设置选项之前 DOM 已更新
+            this.$nextTick(() => {
+                this.updateChart(); // 调用 updateChart 设置新选项
+            });
+        } else {
+            console.error('提供的值无效,未找到对应数据:', val);
+        }
+    },
+    // 把相同的数据加起来
+    sumMinutesByUser(data) {
+      return Object.values(data.reduce((acc, current) => {
+        const { userid, minutes } = current;
+        if (!acc[userid]) {
+          acc[userid] = { ...current, minutes: 0 }; // 初始化用户对象
+        }
+        acc[userid].minutes += minutes; // 累加 minutes
+        return acc;
+      }, {}));
+    },
+    // 把数据处理成盒须图数据
+    processBoxplotData(data) {
+        // 检查数组是否为空
+        if (data.length === 0) return {
+            min: 0,
+            q1: 0,
+            median: 0,
+            q3: 0,
+            max: 0
+        }
+
+        // 提取 minutes 值
+        const minutesArray = data.map(item => item.minutes);
+
+        // 计算盒须图需要的统计值
+        const sortedMinutes = minutesArray.sort((a, b) => a - b);
+        const q1 = sortedMinutes[Math.floor((sortedMinutes.length / 4))];
+        const median = sortedMinutes[Math.floor((sortedMinutes.length / 2))];
+        const q3 = sortedMinutes[Math.floor((sortedMinutes.length * (3 / 4)))];
+        const min = sortedMinutes[0];
+        const max = sortedMinutes[sortedMinutes.length - 1];
+
+        // 盒须图数据格式化
+        return {
+            min: min,
+            q1: q1,
+            median: median,
+            q3: q3,
+            max: max
+        };
+    },
+    initChart() {
+      this.chart = echarts.init(this.$refs.chartContainer)
+      this.updateChart()
+    },
+    handleResize() {
+      this.chart && this.chart.resize();
+    },
+    updateChart() {
+      const chartData = this.chartData.map(item => [
+        item.min, item.q1, item.median, item.q3, item.max
+      ]);
+
+      const option = {
+        tooltip: {},
+        xAxis: {
+          type: 'category',
+          data: this.categories
+        },
+        yAxis: {
+          type: 'value',
+          axisLabel: {
+            formatter: '{value}分钟',
+            color: '#666'
+          },
+        },
+        series: [{
+          type: 'boxplot',
+          data: chartData,
+          itemStyle: {
+            normal: {
+              color: '#5b9bd5'
+            }
+          }
+        }]
+      };
+
+      this.chart.setOption(option);
+      window.addEventListener('resize', this.chart.resize);
+
+    },
+  },
+  beforeDestroy() {
+    window.removeEventListener('resize', this.handleResize)
+    this.chart && this.chart.dispose()
+  },
+};
+</script>
+
+<style scoped>
+/* 这里可以添加样式 */
+.top{
+    width: 100%;
+    display: flex;
+    justify-content: space-between;
+    padding: 10px 20px;
+    box-sizing: border-box;
+    align-items: center;
+}
+.timeL{
+   display: flex;justify-content: space-between;gap: 10px;
+}
+.timeL div{
+    height: 25px;
+    width: 30px;
+    text-align: center;
+    line-height: 25px;
+    box-sizing: border-box;
+    background: #F5F7FA;
+    border-radius: 5px;
+    cursor: pointer;
+}
+.hov{
+  background: #0358D2 !important;color: #fff;
+}
+</style>

+ 131 - 0
src/views/kanBan/components/dataBoardNew/baseData/index.vue

@@ -0,0 +1,131 @@
+<template>
+    <div class="baseDataBox" v-loading="loading">
+       <barList :dataExponentList="dataExponentList"></barList>
+       <div class="conT">
+            <div>
+                <tealiveness :alldata="alldata"/>
+            </div>
+            <div><teahoop :alldata="alldata"/></div>
+       </div>
+       <div class="conT2">
+            <div style="width: 40%;flex-shrink: 0;">
+                <levChart :alldata="alldata"/>
+            </div>
+            <div style="flex: 1;flex-shrink: 0;">
+                <useTimer :alldata="alldata.slice(6)"/>
+            </div>
+       </div>
+    </div>
+</template>
+<script>
+import teahoop from './chartList/teahoop.vue';
+import tealiveness from './chartList/tealiveness.vue';
+import levChart from './chartList/levChart.vue';
+import useTimer from './chartList/useTimer.vue';
+import barList from '../barList.vue';
+import { API_CONFIG } from "@/common/apiConfig";
+
+export default {
+    name:'baseDateChart',
+    props:['oid','org'],
+
+    components: {
+        tealiveness,
+        barList,
+        teahoop,
+        levChart,
+        useTimer
+    },
+    data() {
+        return {
+            loading:false,
+            alldata:[],
+            dataExponentList:[],
+        }
+    },
+    watch:{
+        oid(newL){
+            console.log(newL);
+
+            this.getTeaBaseData()
+        }
+    },
+    methods: {
+        async getTeaBaseData() {
+            this.loading = true;
+            let params = [
+                {
+                    functionName: API_CONFIG.getKanbanBasedata.functionName,
+                    oid: this.oid,
+                    org: '',
+                },
+            ];
+            
+            this.$ajax
+                .post(API_CONFIG.baseUrl, params)
+                .then((res) => {
+                    let top = [
+                        { label: '教师总量', value: 0 },
+                        { label: '学生总量', value: 0 },
+                        { label: '月登录总数', value: 0 },
+                        { label: '月登录环比', value: 0 },
+                    ]
+                    top[0].value = res.data[0][0].num
+                    top[1].value = res.data[1][0].num 
+                    top[2].value = res.data[2][0].num
+
+                    if (res.data[2][0].num == 0 && res.data[3][0].num == 0) {
+                        top[3].value = '0%';
+                    }else{
+                        let nuL = (res.data[2][0].num - res.data[3][0].num) / res.data[3][0].num 
+                        top[3].value = Math.round(nuL * 100) + '%';
+                    }
+
+
+                    this.dataExponentList = top
+
+                    this.alldata = res.data.slice(4)
+                    
+
+                    this.loading = false;
+                })
+                .catch((err) => {
+                    this.loading = false;
+                    console.error(err);
+                });
+        },
+    },
+    mounted() {
+        console.log('getTeaBaseData',this.oid);
+        // if (!this.oid) {
+            this.getTeaBaseData()
+        // }
+    },
+}
+</script>
+<style scoped>
+.baseDataBox{
+    display: flex;
+    flex-direction: column;
+    gap: 20px;
+    padding-bottom: 30px;
+}
+.conT{
+    display: flex;justify-content: space-between;gap: 20px;
+    height: 400px;
+}
+.conT div{
+    flex: 1;flex-shrink: 0;background: #fff;border-radius: 10px;
+    height: 100%;
+}
+.conT2{
+    display: flex;justify-content: space-between;
+    gap: 20px;
+    height: 400px;
+
+}
+.conT2 div{
+    background: #fff;border-radius: 10px;
+    height: 100%;
+}
+</style>

+ 158 - 0
src/views/kanBan/components/dataBoardNew/index.vue

@@ -0,0 +1,158 @@
+<template>
+  <div class="body">
+    <div class="barList">
+        <div @click="cutPage(index)" :class="[pageEnd == index ? 'Selected' : '']" v-for="(i, index) in barList"
+            :key="index">
+            {{ i.name }}
+        </div>
+    </div>
+    <div class="db_bg">
+      <div class="db_body">
+        <baseData v-if="pageEnd == 0" :oid="oid" :org="org" :key="opl"></baseData>
+        <AIApp v-if="pageEnd == 1" :oid="oid" :org="org" :userid="userid" :key="opl"></AIApp>
+        <teaing v-if="pageEnd == 2" :oid="oid" :org="org" :key="opl"></teaing>
+        <teaMange v-if="pageEnd == 3" :oid="oid" :org="org" :key="opl"></teaMange>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import baseData from "./baseData";
+import AIApp from "./AIApp";
+import teaing from "./teaing";
+import teaMange from "./teaMange";
+export default {
+  name:'dataBoardNew',
+  props:['oid','org','userid'],
+  components: {
+    baseData,
+    AIApp,
+    teaing,
+    teaMange,
+  },
+  mounted(){
+    console.log('oid',this.oid);
+  },
+  watch:{
+    oid(newL){
+      console.log(newL);
+      this.opl++
+    }
+  },
+  data() {
+    return {
+      // oid: this.oid,
+      // org: this.org,
+      // userid: this.$route.query.userid,
+      opl:0,
+      barList: [
+          { name: '基础数据', ind: 1 },
+          { name: 'AI应用', ind: 2 },
+          { name: '常规教学', ind: 3 },
+          { name: '教师管理', ind: 4 },
+      ] ,
+      pageEnd: 0
+    };
+  },
+  methods: {
+    cutPage(val) {
+        this.pageEnd = val
+    },
+  }
+};
+</script>
+
+<style scoped>
+.body {
+  height: 100%;
+  width: 100%;
+  overflow: auto;
+  /* min-width: 1550px; */
+  display: flex;
+  flex-direction: column;
+  gap: 20px;
+  box-sizing: border-box;
+}
+
+.db_bg {
+  width: 100% ;
+  border-radius: 8px;
+  overflow: auto;
+  padding: 0 30px;
+  box-sizing: border-box;
+}
+
+
+.logoTop {
+  position: absolute;
+  left: 30px;
+}
+
+
+.db_body {
+  width: 100%;
+}
+.dataExponent {
+    width: 100%;
+    height: 120px;
+    display: grid;
+    background: #fff;
+    border-radius: 20px;
+    grid-template-columns: repeat(4, 1fr);
+    margin-bottom: 20px;
+    align-items: center;
+}
+
+.dataExponentCon {
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    height: 50%;
+    border-right: 1px solid #D9D9D9;
+}
+
+.dataExponentCon:nth-child(4) {
+    border: none;
+}
+
+.dataExponentConTit {
+    font-family: PingFang SC;
+    font-size: 14px;
+    line-height: 100%;
+    color: #969BA3;
+}
+
+.dataExponentConNum {
+    height: 45px;
+    font-family: PingFang SC;
+    font-weight: 600;
+    font-size: 32px;
+    line-height: 45px
+}
+.barList {
+    display: flex;
+    align-items: end;
+    gap: 42px;
+    font-family: PingFang SC;
+    font-weight: 500;
+    font-size: 16px;
+    padding: 0 30px;
+    box-sizing: border-box;
+    height: 50px;
+    color: #969BA3;
+    flex-shrink: 0;
+}
+
+.barList>div {
+    cursor: pointer;
+    height: 23px;
+    line-height: 23px;
+}
+
+.Selected {
+    color: #0663FE;
+    border-bottom: 2px solid #0663FE;
+}
+</style>

+ 113 - 0
src/views/kanBan/components/dataBoardNew/teaMange/chartList/pie2.vue

@@ -0,0 +1,113 @@
+<template>
+  <div ref="chart" class="echart" style="width: 100%;height: 100%;"></div>
+</template>
+
+<script>
+import * as echarts from "echarts";
+
+export default {
+  name:'PieChartComponent',
+  props: ["alldata", "alltest"],
+  data() {
+    return {
+      chart: null,
+      progress: 0
+    };
+  },
+  watch: {
+    alldata: {
+      handler(val) {
+        if (Object.keys(val).length) {
+          this.startCom(val);
+        }
+      },
+      deep: true,
+      immediate: true // 组件创建时立即执行
+    }
+  },
+  // mounted(){
+  //   this.startCom(this.alldata)
+  // },
+  methods: {
+    startCom(val) {
+      let data = JSON.parse(JSON.stringify(val));
+
+      this.progress = (data.is / this.alltest.value).toFixed(2) * 100;
+
+      this.initChart();
+    },
+    initChart() {
+      this.chart = echarts.init(this.$refs.chart);
+      const option = {
+        tooltip: {
+          formatter: "{a} <br/>{b} : {c}%"
+        },
+        series: [
+          {
+            name: "进度",
+            type: "pie",
+            radius: ["50%", "70%"], // 内外半径
+            avoidLabelOverlap: false,
+            label: {
+              show: false,
+              position: "center"
+            },
+
+            startAngle: 90,
+            data: [
+              { value: this.progress, name: "完成" },
+              { value: 100 - this.progress, name: "未完成" }
+            ],
+            color: ["#4caf50", "#e0e0e0"], // 颜色设置
+            itemStyle: {
+              borderRadius: 10,
+              borderColor: "#fff",
+              borderWidth: 2
+            }
+          }
+        ],
+        graphic: [
+          {
+            type: "text",
+            left: "center",
+            top: '43%', // 向下偏移,避免重叠
+            style: {
+              text: `${(this.alldata.is / this.alltest.value).toFixed(2) *
+                100}%`,
+              fill: "#3169D2", // 这里可以设置不同的颜色
+              font: "bold 30px Arial", // 这里可以设置不同的字体大小
+              textAlign: "center",
+            },
+            silent: true
+          },
+          {
+            type: "text",
+            left: "center",
+            top: '52%', // 向下偏移,避免重叠
+            style: {
+              text: "流程完成度",
+              fill: "#6F6869", // 设置另一部分的颜色
+              font: "20px Arial", // 设置另一部分的字体大小
+              textAlign: "center"
+            },
+            silent: true,
+          }
+        ]
+      };
+
+      this.chart.setOption(option);
+      window.addEventListener("resize", this.chart.resize);
+
+    },
+    handleResize() {
+      this.chart && this.chart.resize();
+    }
+  },
+  beforeDestroy() {
+    window.removeEventListener("resize", this.handleResize);
+    this.chart && this.chart.dispose();
+  }
+};
+</script>
+
+<style scoped></style>

+ 141 - 0
src/views/kanBan/components/dataBoardNew/teaMange/chartList/solid.vue

@@ -0,0 +1,141 @@
+<template>
+  <div ref="chart" style="width: 100%; height: 100%"></div>
+</template>
+
+<script>
+import * as echarts from "echarts";
+
+export default {
+  name:'solidChart',
+  props: ["alldata", "alltest"],
+  data() {
+    return {
+      chart: null,
+      chartData: [
+        { value: 0, name: "表单发布" },
+        { value: 0, name: "表单完成" },
+        { value: 0, name: "智能问答" }
+      ]
+    };
+  },
+  watch: {
+    alldata: {
+      handler(val) {
+        if (Object.keys(val).length) {
+          this.startCom(val);
+        }
+      },
+      deep: true,
+      immediate: true // 组件创建时立即执行
+    }
+  },
+    mounted() {
+      this.startCom(this.alldata);
+    },
+  methods: {
+    startCom(val) {
+      let data = JSON.parse(JSON.stringify(val));
+
+      console.log("this.alltest", this.alltest[0].value);
+      console.log("data.noLook", data.noLook);
+
+      this.chartData[0].value =
+        (data.noLook / this.alltest[0].value).toFixed(2) * 100;
+      this.chartData[1].value =
+        (data.is / this.alltest[0].value).toFixed(2) * 100 ;
+      this.chartData[2].value =
+        (this.alltest[2].value / this.alltest[0].value).toFixed(2) * 100;
+
+      console.log("this.chartData", this.chartData);
+
+      this.initChart();
+    },
+    initChart() {
+      this.chart = echarts.init(this.$refs.chart);
+
+      const option = {
+        // tooltip: {
+        //   trigger: "item"
+        // },
+
+        series: [
+          {
+            name: "Funnel",
+            type: "funnel",
+            left: "10%",
+            right: "10%",
+            top: "15%",
+            bottom: "15%",
+            width: "80%",
+            min: 0,
+            max: 100,
+            minSize: "10%",
+            maxSize: "100%",
+            sort: "descending",
+            gap: 10,
+            label: {
+              show: true,
+              position: "inside",
+              fontSize: 15
+            },
+            // labelLine: {
+            //   length: 10,
+            //   lineStyle: {
+            //     width: 1,
+            //     type: "solid"
+            //   }
+            // },
+            // itemStyle: {
+            //   color: this.getColor(index), // 根据索引设置颜色,
+            //   borderColor: "#fff",
+            //   borderWidth: 1
+            // },
+            // emphasis: {
+            //   label: {
+            //     fontSize: 20
+            //   }
+            // },
+            data: this.chartData.map((item, index) => ({
+              value: item.value,
+              name: item.name + item.value + '%',
+              itemStyle: {
+                color: this.getColor(index) // 根据索引设置颜色
+              }
+            }))
+          }
+        ]
+      };
+
+      this.chart.setOption(option);
+      window.addEventListener("resize", this.chart.resize);
+    },
+    getColor(index) {
+      // 颜色渐变从深蓝到浅蓝
+      const colors = ["#68A0F5", "#ABCBF9","#216AD3"]; // 深蓝到浅蓝的颜色数组
+      return colors[index] || colors[colors.length - 1]; // 确保索引在范围内
+    },
+    handleResize() {
+      this.chart && this.chart.resize();
+    }
+  },
+  beforeDestroy() {
+    window.removeEventListener("resize", this.handleResize);
+    this.chart && this.chart.dispose();
+  }
+};
+</script>
+
+<style scoped>
+.data_body {
+  height: 100%;
+  /* display: flex; */
+  position: relative;
+  border-radius: 5px;
+  /* border: 1px solid #eee; */
+  margin: 0 auto;
+  box-sizing: border-box;
+  padding: 0;
+  width: 95%;
+  /* background: #fff; */
+}
+</style>

+ 147 - 0
src/views/kanBan/components/dataBoardNew/teaMange/index.vue

@@ -0,0 +1,147 @@
+<template>
+    <div class="baseDataBox" v-loading="isLoadingData">
+        <barList :dataExponentList="dataExponentList"></barList>
+        <div class="DataDiv">
+            表单流程转化
+            <div class="imgArea" style="display: flex;height: 100%;">
+                <solid :alldata="dataJ" :alltest="dataExponentList"/>
+                <pie2 :alldata="dataJ" :alltest="dataExponentList[0]"/>
+            </div>
+        </div>
+           
+    </div>
+</template>
+<script>
+import solid from './chartList/solid.vue';
+import pie2 from './chartList/pie2.vue';
+import barList from '../barList.vue';
+import { API_CONFIG } from "@/common/apiConfig";
+
+export default {
+    name:'TeaMangeChart',
+    props: {
+        oid: {
+            type: String,
+        },
+        org: {
+            type: String,
+        }
+    },
+    components: {
+        solid,
+        pie2,
+        barList
+    },
+    data() {
+        return {
+            dataExponentList:[],
+            dataJ:{},
+            isLoadingData:false
+        }
+    },
+    watch:{
+        oid(newL){
+            console.log(newL);
+            
+            this.getData()
+        }
+    },
+    mounted() {
+        this.getData()
+    },
+    methods: {
+        getData() {
+            this.isLoadingData = true;
+            let params = [{
+                functionName: API_CONFIG.getKanbanTeaManagedata.functionName,
+                oid: this.oid,
+                org: this.org,
+            }];
+            this.$ajax
+                .post(API_CONFIG.baseUrl, params)
+                .then((res) => {
+                    let test = res.data[2]
+
+                    let dataJ = {
+                        doing: 0,    //进行中
+                        nodo: 0,     //未进行
+                        noLook: 0,    //未发布
+                        is: 0,     //已完成
+                        no: 0         //已逾期
+                    }
+                    for (var i = 0; i < test.length; i++) {
+                        if (test[i].look == '1') {
+                            dataJ.noLook++
+                            // console.log('index-----------', i)
+                        }
+                        let now = new Date().getTime()
+                        let isTime = test[i].over_at && new Date(test[i].over_at).getTime()
+                        if (test[i].over_at && test[i].look == '2' && now > isTime && ((test[i].isUserCount > 0 && test[i].isUserCount > test[i].isWorkCount) || test[i].isUserCount == 0)) {
+                            dataJ.no++
+                            // console.log('index-----------', i)
+                        }
+
+                        if (((test[i].over_at && test[i].look == '2' && isTime > now) || (!test[i].over_at && test[i].look == '2')) && ((test[i].isUserCount == 0 && test[i].isWorkCount > 0) || (test[i].isUserCount > 0 && 0 < test[i].isWorkCount && test[i].isWorkCount < test[i].isUserCount))) {
+                            dataJ.doing++
+                            // console.log('index-----------', i)
+                        }
+
+
+                        if (((test[i].over_at && test[i].look == '2' && isTime > now) || (!test[i].over_at && test[i].look == '2')) && test[i].isWorkCount == 0) {
+                            dataJ.nodo++
+                            // console.log('index-----------', i)
+                        }
+
+                        if (test[i].look == '2' && (test[i].isUserCount > 0 && test[i].isUserCount <= test[i].isWorkCount)) {
+                            dataJ.is++
+                            // console.log('index-----------', i)
+                        }
+                    }
+                    this.dataJ = dataJ
+                    console.log('dataj',dataJ);
+
+                    let top = [
+                        { label: '表单总量', value: 0 },
+                        { label: '表单回收率', value: 0 },
+                        { label: '智能回答次数', value: 0 }
+                    ]
+
+                    top[0].value = res.data[0][0].num
+                    top[1].value = ((this.dataJ.is / res.data[0][0].num).toFixed(0)) * 100 + '%'
+                    top[2].value = res.data[1][0].num //本月新增
+
+                    this.dataExponentList = top
+                    this.isLoadingData = false;
+
+
+                })
+                .catch((err) => {
+                    this.isLoadingData = false;
+                    console.error(err);
+                });
+        },
+    },
+
+}
+</script>
+<style scoped>
+
+.baseDataBox{
+    display: flex;
+    flex-direction: column;
+    gap: 20px;
+}
+.DataDiv {
+    width: 100%;
+    height: 500px;
+    border-radius: 5px;
+    margin: 0 0 10px 0;
+    background-color: #fff;
+    border: 2px solid #fff;
+    box-sizing: border-box;
+    padding: 20px;
+    box-sizing: border-box;
+}
+
+
+</style>

+ 125 - 0
src/views/kanBan/components/dataBoardNew/teaing/chartList/BubbleChart.vue

@@ -0,0 +1,125 @@
+
+<template>
+  <div style="display: flex;flex-direction: column;height: 100%;">
+    课堂分布
+    <div ref="chart" style="width: 100%; flex: 1;"></div>
+  </div>
+</template>
+
+<script>
+import * as echarts from 'echarts';
+
+export default {
+  props: {
+    subjects: { type: Array, default: () => [] },  // 学科数组
+    grades: { type: Array, default: () => [] },    // 年级数组
+    courses: { type: Array, default: () => [] }    // 课程数组
+  },
+  data() {
+    return {
+      chart: null,
+      allData:[],
+      subList:[],
+      graList:[],
+      couList:[],
+      chartData:[]
+    };
+  },
+  watch: {
+    subjects: {
+      handler() {
+        let allData = JSON.parse(JSON.stringify(this.grades))
+
+        allData.forEach(e=>{
+          e.subCh = JSON.parse(JSON.stringify(this.subjects))
+          for (let i = 0; i < e.subCh.length; i++) {
+            e.subCh[i].sum = 0
+          }
+
+          for (let index = 0; index < e.subCh.length; index++) {
+            for (let i = 0; i < this.courses.length; i++) {
+              if (this.courses[i] && this.courses[i].typeids && this.courses[i].typeids.includes(e.id) && this.courses[i].typeids.includes(e.subCh[index].id)) {
+                e.subCh[index].sum++
+              }
+            }
+          }
+        })
+
+        allData.forEach((grade,gid) => {
+          grade.subCh.forEach((sub,pid) => {
+            // 只展示 sum > 0 的点(气泡图一般这样处理)
+            // if (sub.sum > 0) {
+             this.chartData.push([gid, pid, sub.sum,sub.name, grade.name]);
+            // }
+          });
+        });
+
+        // console.log('this.chartData',this.chartData);
+
+        this.initChart();
+
+      },
+      deep: true
+    }
+  },
+
+  mounted() {
+    this.initChart();
+  },
+  beforeDestroy() {
+    window.removeEventListener('resize', this.handleResize);
+    this.chart && this.chart.dispose();
+  },
+  methods: {
+    initChart() {
+      this.chart = echarts.init(this.$refs.chart);
+      this.updateChart();
+    },
+    updateChart() {
+      const option = {
+        tooltip: {
+          formatter: params => {
+            return `${params.data[3]}<br/>年级: ${params.data[4]}<br/>课程数量: ${params.data[2]}`;
+          }
+        },
+        legend: { 
+          data: [...new Set(this.chartData.map(item => item[0]))],
+          right: 10,
+          top: 20
+        },
+        xAxis: {
+          type: 'category',
+          name: '年级',
+          data: [...new Set(this.grades.map(item => item.name))],
+          axisLabel: { interval: 0, rotate: 30 }
+        },
+        yAxis: {
+          type: 'category',
+          name: '学科',
+          data: [...new Set(this.subjects.map(item => item.name))]
+        },
+        series: [{
+          type: 'scatter',
+          symbolSize: function(data) {
+            return data[2] * 4; // 气泡大小与课程数量成正比
+          },
+          data: this.chartData,
+
+          emphasis: {
+            itemStyle: {
+              shadowBlur: 10,
+              shadowColor: 'rgba(0, 0, 0, 0.5)'
+            }
+          }
+        }]
+      };
+      this.chart.setOption(option);
+      window.addEventListener('resize', this.chart.resize);
+    },
+    handleResize() {
+      this.chart && this.chart.resize();
+    }
+  },
+  
+};
+</script>

+ 135 - 0
src/views/kanBan/components/dataBoardNew/teaing/chartList/aidedDesign.vue

@@ -0,0 +1,135 @@
+<template>
+  <div ref="chart" class="chart-container"></div>
+</template>
+
+<script>
+import * as echarts from 'echarts';
+
+export default {
+  name: 'NestedPieChart',
+  props: ['alldata','subject'],
+  data() {
+    return {
+      chart: null,
+      innerData: [
+        { value: 0, name: 'AI模式' },
+        { value: 0, name: '上课模式' }
+      ],
+      outerData: []
+    };
+  },
+  watch: {
+     alldata:{
+          handler(val) {                
+              if (val && val.length) {
+                  this.startCom(val);
+              }
+          },
+          deep:true,
+          immediate: true  // 组件创建时立即执行
+      }
+  },
+  // mounted() {
+  //   this.startCom(this.alldata);
+  // },
+  beforeDestroy() {
+    window.removeEventListener('resize', this.handleResize)
+    this.chart && this.chart.dispose();
+  },
+  methods: {
+    startCom(val) {
+      let data = JSON.parse(JSON.stringify(val));
+      // console.log('subject',this.subject);
+      
+      // console.log('data',data);
+
+      this.innerData[0].value = data.filter(e=> e.state == 5 || e.state == 6).length
+      this.innerData[1].value = data.filter(e=> e.state != 5 && e.state != 6).length
+
+      // 为每个对象添加一个 value 属性
+      let kk = this.subject.map(item => ({
+        ...item, // 保留原有属性
+        value: 0 // 添加 value 属性,设为默认值
+      }));
+
+      this.outerData = kk.map(category => {
+        const count = data.reduce((acc, item) => {
+          return acc + ((item.typeids && item.typeids.includes(category.id)) ? 1 : 0);
+        }, 0);
+        return {
+          ...category,
+          value: count // 添加计算的数量
+        };
+      });
+        this.initChart();
+    },
+    initChart() {
+      this.chart = echarts.init(this.$refs.chart);
+      const options = {
+        tooltip: {
+          trigger: 'item'
+        },
+        grid: {
+          top: '10%', // 调整图表的上边距
+          // left: '3%',
+          // right: '4%',
+          // bottom: '3%',
+          containLabel: true
+        },
+        legend: {
+          bottom:'0%',
+          left:'5%',
+          right:'5%'
+        },
+        series: [
+          {
+            name: '模式分类',
+            type: 'pie',
+            center: ['50%', '40%'], // 调整饼图的中心位置
+            radius: ['0%', '30%'],
+            label: {
+              show: false,
+              position: 'center'
+            },
+            data: this.innerData,
+            itemStyle: {
+              borderRadius: 8,
+              borderColor: '#fff',
+              borderWidth: 2
+            }
+          },
+          {
+            name: '学科分类',
+            type: 'pie',
+            center: ['50%', '40%'], // 调整饼图的中心位置
+            radius: ['40%', '60%'],
+            label: {
+              show: false
+            },
+            data: this.outerData,
+            itemStyle: {
+              borderRadius: 8,
+              borderColor: '#fff',
+              borderWidth: 2
+            }
+          }
+        ]
+      };
+
+      this.chart.setOption(options);
+      window.addEventListener('resize', this.chart.resize);
+
+    },
+    handleResize() {
+      this.chart && this.chart.resize();
+    }
+  }
+};
+</script>
+
+<style scoped>
+.chart-container {
+  width: 100%;
+  height: 400px;
+}
+</style>

+ 179 - 0
src/views/kanBan/components/dataBoardNew/teaing/chartList/appUseAss.vue

@@ -0,0 +1,179 @@
+<template>
+  <div ref="chartContainer" style="width: 100%; height: 100%;"></div>
+</template>
+
+<script>
+import * as echarts from "echarts";
+
+export default {
+  //   name: 'SubjectScatterChart',
+  props: ["alldata", "subject"],
+  // props: {
+  //   // 数据格式:[{ subject: '语文', value: [x, y] }, ...]
+  //   chartData: {
+  //     type: Array,
+  //     default: () => [
+  //       { subject: '语文', value: [10.0, 8.04] },
+  //       { subject: '数学', value: [8.07, 6.95] },
+  //       { subject: '英语', value: [13.0, 7.58] },
+  //     ]
+  //   },
+  //   // 学科颜色配置
+  //   colors: {
+  //     type: Object,
+  //     default: () => ({
+  //       语文: '#5470C6',
+  //       数学: '#91CC75',
+  //       英语: '#EE6666'
+  //     })
+  //   },
+  //   symbolSize: {
+  //     type: Number,
+  //     default: 16
+  //   }
+  // },
+  data() {
+    return {
+      chart: null,
+      chartData: []
+    };
+  },
+  mounted() {
+    this.startCom(this.alldata);
+    // window.addEventListener('resize', this.handleResize);
+  },
+  beforeDestroy() {
+    window.removeEventListener("resize", this.handleResize);
+    this.chart && this.chart.dispose();
+  },
+  watch: {
+    alldata: {
+      handler(val) {
+        if (val.length) {
+          this.startCom(val);
+        }
+      },
+      deep: true,
+      immediate: true // 组件创建时立即执行
+    }
+  },
+  methods: {
+    startCom(val) {
+      let data = JSON.parse(JSON.stringify(val));
+      let subject = JSON.parse(JSON.stringify(this.subject));
+
+      data.forEach(e => {
+        e.aitool = 0;
+        e.chapters = JSON.parse(e.chapters);
+        let kop = [];
+        kop = e.chapters.map(i => i.chapterInfo[0].taskJson);
+        let op = kop.map(k => k.map(l => l.toolChoose).flat()).flat();
+        e.aitool = op
+          .map(m => {
+            if (m && m.tool && m.tool.length) {
+              return m.tool[0];
+            }
+          })
+          .filter(num => num === 72).length;
+      });
+
+      console.log("val", data);
+      console.log("subject", subject);
+
+      subject.forEach(e => {
+        let course = [];
+        let work = [];
+        e.aitoolsum = 0;
+        e.worksum = 0;
+        data.forEach(k => {
+          if (k.typeids && k.typeids.includes(e.id)) {
+            course.push(k.aitool);
+            work.push(k.num);
+          }
+        });
+
+        e.worksum = work.reduce((acc, curr) => acc + curr, 0);
+        e.aitoolsum = course.reduce((acc, curr) => acc + curr, 0);
+      });
+
+      this.chartData = subject.map(e => ({
+        subject: e.name,
+        value: [e.aitoolsum, e.worksum]
+      }));
+      console.log("this.chartData", this.chartData);
+
+      this.initChart();
+    },
+
+    initChart() {
+      this.chart = echarts.init(this.$refs.chartContainer);
+      this.updateChart();
+    },
+    generateSeries() {
+      // 按学科分组数据
+      const subjects = [...new Set(this.chartData.map(d => d.subject))];
+      return subjects.map(subject => ({
+        name: subject,
+        type: "scatter",
+        data: this.chartData
+          .filter(d => d.subject === subject)
+          .map(d => d.value)
+        // itemStyle: {
+        //   color: this.colors[subject] || '#999'
+        // },
+        // symbolSize: this.symbolSize
+      }));
+    },
+    updateChart() {
+      const option = {
+        tooltip: {
+          trigger: "item",
+          formatter: params => {
+            return `${params.seriesName}<br/>
+                    X: ${params.value[0].toFixed(2)}<br/>
+                    Y: ${params.value[1].toFixed(2)}`;
+          }
+        },
+        legend: {
+          data: [...new Set(this.chartData.map(d => d.subject))],
+          // orient: "horizontal", // 水平布局
+          left: "center", // 水平居中
+          top: "0%", // 距离顶部10%
+          padding: [10, 10] // 内边距
+        },
+        grid: {
+          top: '23%',      // 上边距,给图表留出空间
+        },
+        xAxis: {
+          name: "单节课程AI迁入数量",
+          type: "value",
+          scale: true,
+          nameLocation: "middle", // 将名称放在轴的中间
+          nameGap: 30, // 设置名称与轴之间的间距
+          nameTextStyle: {
+            align: "middle", // 使名称居中对齐
+            fontSize: 16 // 设置字体大小
+          }
+        },
+        yAxis: {
+          name: "学生交互量",
+          type: "value",
+          scale: true,
+          nameLocation: "middle", // 将名称放在轴的中间
+          nameGap: 30, // 设置名称与轴之间的间距
+          nameTextStyle: {
+            align: "middle", // 使名称居中对齐
+            fontSize: 16 // 设置字体大小
+          }
+        },
+        series: this.generateSeries()
+      };
+      this.chart.setOption(option, true);
+      window.addEventListener("resize", this.chart.resize);
+    },
+    handleResize() {
+      this.chart && this.chart.resize();
+    }
+  }
+};
+</script>

+ 240 - 0
src/views/kanBan/components/dataBoardNew/teaing/index.vue

@@ -0,0 +1,240 @@
+<template>
+  <div class="body1" v-loading="isLoading">
+    <barList :dataExponentList="dataExponentList"></barList>
+    <div class="AIAppOne">
+      <div>
+        <BubbleChart :grades="alldata[1]" :subjects="alldata[2]" :courses="alldata[3]"></BubbleChart>
+      </div>
+      <div style="display: flex;flex-direction: column;">
+        课程复用Top5
+         <div class="hotCon">
+          <div v-for="(i,index) in alldata[5]" :key="index" class="hotConA">
+            <div class="hotConAA">
+              <span>{{ index + 1 }}</span>
+              <div>{{ i.title }}</div>
+            </div>
+            <div style="color: #3072D8;">{{ i.opennum }}次</div>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <div class="AIAppTwo">
+      <div class="AIAppOneCon">
+        学生参与Top5
+        <div class="hotCon">
+          <div v-for="(i,index) in alldata[4]" :key="index" class="hotConA">
+            <div class="hotConAA">
+              <span>{{ index + 1 }}</span>
+              <div>{{ i.title }}</div>
+            </div>
+            <div style="color: #3072D8;">{{ i.num }}次</div>
+          </div>
+        </div>
+      </div>
+      <div class="AIAppOneCon">
+        AI辅助设计分析
+        <aidedDesign :alldata="alldata[0]" :subject="alldata[2]"></aidedDesign>
+      </div>
+      <div class="AIAppOneCon">
+        AI应用使用分析
+        <appUseAss :alldata="alldata[0]" :subject="alldata[2]"></appUseAss>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+  import BubbleChart from "./chartList/BubbleChart.vue";
+  import aidedDesign from "./chartList/aidedDesign.vue";
+  import appUseAss from "./chartList/appUseAss.vue";
+  import barList from '../barList.vue';
+import { API_CONFIG } from "@/common/apiConfig";
+  
+export default {
+  name:'TeaingChartComponent',
+  components: {
+    BubbleChart,
+    aidedDesign,
+    appUseAss,
+    barList
+  },
+  props: {
+    oid: {
+      type: String,
+    },
+    org: {
+      type: String,
+    }
+  },
+
+  data() {
+    return {
+      alldata:[],
+      dataExponentList:[],
+      isLoading:false,
+    };
+  },
+  mounted() {
+    this.getTeaData()
+  },
+  watch:{
+        oid(newL){
+            console.log(newL);
+
+            this.getTeaData()
+        }
+    },
+  methods: {
+    async getTeaData() {
+      this.isLoading = true;
+      let params = [
+        {
+          functionName: API_CONFIG.getKanbanTeadata.functionName,
+          oid: this.oid,
+          org: '',
+        },
+      ];
+      
+      this.$ajax
+        .post(API_CONFIG.baseUrl, params)
+        .then((res) => {
+            let top = [
+              { label: '课程总量', value: 0 },
+              { label: '本月新增课程', value: 0 },
+              { label: '新增课程环比', value: 0 },
+              { label: 'AI应用嵌入数量', value: 0 },
+            ]
+
+            top[0].value = res.data[0][0].num
+            top[1].value = res.data[1][0].num //本月新增
+            //上月新增
+            if(res.data[1][0].num ==0 && res.data[2][0].num ==0){
+              top[2].value = '0%'
+            }else{
+              top[2].value = ((res.data[1][0].num - res.data[2][0].num) / res.data[2][0].num).toFixed(2) * 100 + '%'
+            }
+            
+            // 课程所有阶段整合
+            let k =  this.Aitool(res.data[3])
+            top[3].value = k
+
+            this.dataExponentList = top
+
+            this.alldata = res.data.slice(3)
+            // console.log('getTeaData',this.alldata);
+            
+            this.isLoading = false;
+        })
+        .catch((err) => {
+            this.isLoading = false; 
+            console.error(err);
+        });
+    },
+    Aitool(val){
+        // 一层一层往下剥,最后得出所有工具的数组
+        let data = val.map(e=> JSON.parse(e.chapters)).flat(); 
+        let a = data.map(e => e.chapterInfo[0].taskJson).flat()
+        let b = a.map(e => {
+          if (e.toolChoose && e.toolChoose.length) { 
+            return e.toolChoose
+          }
+        }).flat()
+        
+        let alltool = b.map(e=>{ 
+          if (e && e.tool && e.tool.length) {
+            return e.tool[0]
+          }
+        }).filter(num => num === 72).length
+        console.log('alltool',alltool); 
+
+        return  alltool
+    }
+  },
+};
+</script>
+
+
+<style scoped>
+.body1 {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  gap: 20px;
+  box-sizing: border-box;
+  overflow: hidden;
+  position: relative;
+  padding-bottom: 30px;
+}
+.AIAppOne{
+  width: 100%;
+  display: grid;
+  grid-template-columns: repeat(2, 1fr);  /* 3列网格 */
+  height: 550px;
+  gap: 20px;  /* 间距 */
+}
+.AIAppOne > div{
+  padding: 20px;
+  border-radius: 10px;
+  box-sizing: border-box;
+  background: #fff;
+  box-sizing: border-box;
+}
+
+.AIAppOneCon{
+  padding: 20px;
+  border-radius: 10px;
+  box-sizing: border-box;
+  background: #fff;
+  display: flex;
+  flex-direction: column;
+}
+
+.AIAppTwo{
+  width: 100%;
+  display: grid;
+  grid-template-columns: repeat(3, 1fr);  /* 3列网格 */
+  min-height: 480px;
+  gap: 20px;  /* 间距 */
+}
+.footCon{
+  width: 100%;
+  padding: 20px;
+  border-radius: 10px;
+  box-sizing: border-box;
+  background: #fff;
+  height: 350px;
+}
+.hotCon{
+  display: flex;
+  flex: 1;
+  flex-direction: column;
+  background: #F8FAFC;
+  padding: 20px 15px;
+  box-sizing: border-box;
+  border-radius: 8px;
+  margin-top: 10px;
+}
+.hotConA{
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 8px 0;
+  height: 30px;
+  border-bottom:1px #e7e7e7 solid;
+}
+.hotConAA{
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+.hotConAA > span{
+  padding: 0px 5px;
+  color: #fff;
+  margin-right: 5px;
+  box-sizing: border-box;
+  background: #0358D2;
+  border-radius: 3px;
+}
+</style>

+ 7 - 3
src/views/kanBan/index.vue

@@ -51,7 +51,8 @@
 
                 <div class="right_content">
                     <div class="Con" v-if="isActive === 1">
-                        <AdataKanban></AdataKanban>
+                        <!-- <AdataKanban></AdataKanban> -->
+                         <dataBoardNew :oid="schId" :org="schOrg" :userid="userinfo.userid"/>
                     </div>
                     <div class="Con" v-if="isActive === 2">
                         <AschoolFeature :schId="schId" :schOrg="schOrg"></AschoolFeature>
@@ -67,7 +68,9 @@
 </template>
 <script>
 import schoolArea from '../../components/schoolArea.vue';
-import AdataKanban from './components/AdataKanban';
+// import AdataKanban from './components/AdataKanban';
+import dataBoardNew from './components/dataBoardNew';
+
 import AquickEntrance from './components/AquickEntrance';
 import AschoolFeature from './components/AschoolFeature';
 import { mapGetters, mapActions } from 'vuex';
@@ -78,7 +81,8 @@ export default {
     name:'kanBan',
     components: {
         schoolArea,
-        AdataKanban,
+        // AdataKanban,
+        dataBoardNew,
         AquickEntrance,
         AschoolFeature
     },