529 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			529 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <template>
 | ||
|   <section class="AppAiStatistics">
 | ||
|     <ai-detail list>
 | ||
|       <ai-title slot="title" title="AI调用统计">
 | ||
|         <template #rightBtn>
 | ||
|           <el-row type="flex" align="middle">
 | ||
|             <!-- <el-cascader ref="cascader1" clearable v-model="totalDeptList" :options="deptOptions" placeholder="所属部门" size="small"
 | ||
|               :props="defaultProps" :show-all-levels="false" @change="totalDeptSelect"></el-cascader> -->
 | ||
|               <AiAreaGet style="width: 250px" clearable always-show :instance="instance" v-model="totalAreaId" placeholder="所属地区" @change="getTotal"></AiAreaGet>
 | ||
|           </el-row>
 | ||
|         </template>
 | ||
|       </ai-title>
 | ||
|       <template #content>
 | ||
|         <div class="card_list">
 | ||
|           <div class="card">
 | ||
|             <h2>累计调用次数</h2>
 | ||
|             <p class="color1">{{ totalInfo['累计'] || 0 }}</p>
 | ||
|           </div>
 | ||
|           <div class="card">
 | ||
|             <h2>本月调用次数</h2>
 | ||
|             <p class="color1">{{ totalInfo['本月'] || 0 }}</p>
 | ||
|           </div>
 | ||
|           <div class="card">
 | ||
|             <h2>本周调用次数</h2>
 | ||
|             <p class="color1">{{ totalInfo['本周'] || 0 }}</p>
 | ||
|           </div>
 | ||
|           <div class="card">
 | ||
|             <h2>昨日调用次数</h2>
 | ||
|             <p class="color1">{{ totalInfo['昨日'] || 0 }}</p>
 | ||
|           </div>
 | ||
|         </div>
 | ||
|         <ai-title slot="title" :title="`AI调用分析(${totalEcount}次)`">
 | ||
|           <template #rightBtn>
 | ||
|             <el-row type="flex" align="middle">
 | ||
|               <span class="shortcut" v-for="(item,i) in timeCheck" :key="i" :class="{active:type==i}"
 | ||
|                 @click="timeChange(i)" v-text="item"/>
 | ||
|               <!-- <el-cascader ref="cascader2" clearable v-model="deptList" :options="deptOptions" placeholder="所属部门" size="small"
 | ||
|                 :props="defaultProps" :show-all-levels="false" @change="deptSelect"></el-cascader> -->
 | ||
|               <AiAreaGet style="width: 250px" clearable always-show :instance="instance" v-model="areaId" placeholder="所属地区" @change="getStatistics"></AiAreaGet>
 | ||
|             </el-row>
 | ||
|           </template>
 | ||
|         </ai-title>
 | ||
|         <el-row type="flex" class="mar-t4 gap-20 chart-content">
 | ||
|           <div class="chartBox fill">
 | ||
|             <b>AI调用趋势图</b>
 | ||
|             <div id="trendChart" style="height: 280px; width: 100%;" v-if="trendData.length" class="chart"></div>
 | ||
|             <ai-empty v-else style="height: 200px; width: 100%;" id="empty"/>
 | ||
|           </div>
 | ||
|           <div class="chartBox fill">
 | ||
|             <b>AI调用群聊排行榜</b>
 | ||
|             <ai-table v-if="tableData.length"
 | ||
|               :tableData="tableData"
 | ||
|               :col-configs="colConfigs"
 | ||
|               :isShowPagination="false"
 | ||
|               style="margin-top: 6px; width: 100%; height: 280px;">
 | ||
|             </ai-table>
 | ||
|             <ai-empty v-else style="height: 200px; width: 100%;" id="empty"/>
 | ||
|           </div>
 | ||
|         </el-row>
 | ||
|         <el-row type="flex" class="mar-t4 gap-20">
 | ||
|           <div class="chartBox fill">
 | ||
|             <b>问答分类统计</b>
 | ||
|             <div>
 | ||
|               <div id="barChart" style="height: 260px; width: 100%;" v-if="barData.length"></div>
 | ||
|               <ai-empty v-else style="height: 200px; width: 100%;" id="empty"/>
 | ||
|             </div>
 | ||
|           </div>
 | ||
|           <div class="chartBox fill">
 | ||
|             <b>问答分类词云</b>
 | ||
|             <div>
 | ||
|               <div id="wordChart" style="height: 260px; width: 100%;" v-if="wordData.length"/>
 | ||
|               <ai-empty v-else style="height: 200px; width: 100%;" id="empty"/>
 | ||
|             </div>
 | ||
|           </div>
 | ||
|         </el-row>
 | ||
|         <ai-dialog :visible.sync="dialogDate" title="选择时间" width="500px" customFooter>
 | ||
|           <el-date-picker v-model="timeList" size="small" type="daterange" value-format="yyyy-MM-dd"
 | ||
|             range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期">
 | ||
|           </el-date-picker>
 | ||
|           <el-button slot="footer" @click="selectDete" type="primary">确认</el-button>
 | ||
|         </ai-dialog>
 | ||
|       </template>
 | ||
|     </ai-detail>
 | ||
|   </section>
 | ||
| </template>
 | ||
| 
 | ||
| <script>
 | ||
| import {mapState} from "vuex"
 | ||
| ;
 | ||
| import AiDetail from "dui/packages/layout/AiDetail.vue";
 | ||
| import AiTitle from "dui/packages/basic/AiTitle.vue";
 | ||
| import "echarts-wordcloud";
 | ||
| 
 | ||
| export default {
 | ||
|   name: "AppAiStatistics",
 | ||
|   components: {AiTitle, AiDetail},
 | ||
|   label: "AI调用统计",
 | ||
|   props: {
 | ||
|     instance: Function,
 | ||
|     dict: Object,
 | ||
|     permissions: Function,
 | ||
|   },
 | ||
|   data() {
 | ||
|     return {
 | ||
|       defaultProps: {
 | ||
|         label: 'name',
 | ||
|         value: 'fullId',
 | ||
|         checkStrictly: true,
 | ||
|       },
 | ||
|       deptOptions: [],
 | ||
|       info: {},
 | ||
|       trendData: [],
 | ||
|       trendChart: null,
 | ||
|       barData: [],
 | ||
|       barChart: null,
 | ||
|       wordData: [],
 | ||
|       wordChart: null,
 | ||
|       tableData: [],
 | ||
|       totalInfo: {},
 | ||
|       type: '0',
 | ||
|       timeCheck: ['近7天', '近30天', '近1年', '自定义'],
 | ||
|       dialog: false,
 | ||
|       dialogDate: false,
 | ||
|       timeList: [],
 | ||
|       startTime: '',
 | ||
|       endTime: '',
 | ||
|       totalDeptList: [],
 | ||
|       deptList: [],
 | ||
|       time: [],
 | ||
|       totalAreaId: '',
 | ||
|       areaId: '',
 | ||
|       totalEcount: 0
 | ||
|     }
 | ||
|   },
 | ||
|   computed: {
 | ||
|     ...mapState(['user']),
 | ||
|     colConfigs() {
 | ||
|       return [
 | ||
|         {prop: "rank", label: '排名', align: "center", width: "80px"},
 | ||
|         {prop: "roomName", label: '群名称', align: "center"},
 | ||
|         // {prop: "ownerName", label: '群主', align: "center", width: "100px"},
 | ||
|         {prop: "c", label: '触发数', align: "center", width: "100px"},
 | ||
|       ]
 | ||
|     },
 | ||
|   },
 | ||
|   created() {
 | ||
|     this.getDeptList()
 | ||
|     this.getTotal()
 | ||
|     this.getStatistics()
 | ||
|   },
 | ||
|   methods: {
 | ||
|     getTotal() {
 | ||
|       // var totalDepartmentId = this.totalDeptList.length ? this.totalDeptList[this.totalDeptList.length-1] : ''
 | ||
|       this.instance.post(`/app/appmasssendingtaskbaidu/statistics1?areaId=${this.totalAreaId || ''}`).then(res => {
 | ||
|         if (res?.data) {
 | ||
|           console.log(res)
 | ||
|           this.totalInfo = res.data
 | ||
|         }
 | ||
|       })
 | ||
|     },
 | ||
|     getStatistics() {
 | ||
|       this.trendData = [], this.barData = [], this.wordData = [], this.tableData = []
 | ||
|       // var departmentId = this.deptList.length ? this.deptList[this.deptList.length-1] : ''
 | ||
|       this.instance.post('/app/appmasssendingtaskbaidu/statistics2', null, {
 | ||
|         params: {
 | ||
|           // deptFullId: departmentId,
 | ||
|           areaId: this.areaId,
 | ||
|           type: this.type,
 | ||
|           startTime: this.startTime,
 | ||
|           endTime: this.endTime,
 | ||
|         }
 | ||
|       }).then(res => {
 | ||
|         if (res?.data) {
 | ||
|           this.info.trend = res.data.trend
 | ||
|           var trendX = []
 | ||
|           this.totalEcount = 0
 | ||
|           this.info.trend.map((item) => {
 | ||
|             trendX.push(item.ymd)
 | ||
|             this.totalEcount = this.totalEcount + item.ecount
 | ||
|             this.trendData.push(item.ecount)
 | ||
|           })
 | ||
|           if(this.trendData.length) {
 | ||
|             this.$nextTick(() => {
 | ||
|               this.trendChartInit(trendX, this.trendData)
 | ||
|             })
 | ||
|           }
 | ||
|         }
 | ||
|       })
 | ||
| 
 | ||
|       this.instance.post('/app/appmasssendingtaskbaidu/statistics3', null, {
 | ||
|         params: {
 | ||
|           areaId: this.areaId,
 | ||
|           type: this.type,
 | ||
|           startTime: this.startTime,
 | ||
|           endTime: this.endTime,
 | ||
|         }
 | ||
|       }).then(res => {
 | ||
|         if (res?.data) {
 | ||
|           this.info.ranking = res.data.ranking
 | ||
|           this.info.ranking.map((item, index)=> {
 | ||
|             if(index < 100) {
 | ||
|               item.rank = index+1
 | ||
|               this.tableData.push(item)
 | ||
|             }
 | ||
|           })
 | ||
| 
 | ||
|         }
 | ||
|       })
 | ||
| 
 | ||
|       this.instance.post('/app/appmasssendingtaskbaidu/statistics5', null, {
 | ||
|         params: {
 | ||
|           areaId: this.areaId,
 | ||
|           type: this.type,
 | ||
|           startTime: this.startTime,
 | ||
|           endTime: this.endTime,
 | ||
|         }
 | ||
|       }).then(res => {
 | ||
|         if (res?.data) {
 | ||
|           if(res.data.length) {
 | ||
|             var barX = []
 | ||
|             res.data.map((item, index) => {
 | ||
|               if(index < 10) {
 | ||
|                 barX.push(item.tag)
 | ||
|                 this.barData.push(item.c)
 | ||
|               }
 | ||
|               var i = { name: item.tag, value: item.c };
 | ||
|               this.wordData.push(i);
 | ||
|             })
 | ||
|             if(this.barData.length) {
 | ||
|               this.$nextTick(() => {
 | ||
|                 this.barChartInit(barX, this.barData)
 | ||
|               })
 | ||
|             }
 | ||
|             if(this.wordData.length) {
 | ||
|               this.$nextTick(() => {
 | ||
|                 this.wordChartInit(this.wordData)
 | ||
|               })
 | ||
|             }
 | ||
|           } 
 | ||
|         }
 | ||
|       })
 | ||
|     },
 | ||
|     trendChartInit(xData, yData) {
 | ||
|       this.trendChart = echarts.init(document.getElementById('trendChart'))
 | ||
|       let option = {
 | ||
|         xAxis: {
 | ||
|           type: 'category',
 | ||
|           data: xData
 | ||
|         },
 | ||
|         yAxis: {
 | ||
|           type: 'value'
 | ||
|         },
 | ||
|         grid: {
 | ||
|           left: '10px',
 | ||
|           right: '28px',
 | ||
|           bottom: '14px',
 | ||
|           top: '30px',
 | ||
|           containLabel: true
 | ||
|         },
 | ||
|         tooltip: {
 | ||
|           trigger: 'axis'
 | ||
|         },
 | ||
|         legend: {
 | ||
|           type: "plain"
 | ||
|         },
 | ||
|         color: '#2891FF',
 | ||
|         series: [
 | ||
|           {
 | ||
|             data: yData,
 | ||
|             type: 'line'
 | ||
|           }
 | ||
|         ]
 | ||
|       }
 | ||
|       this.trendChart.setOption(option)
 | ||
|     },
 | ||
|     barChartInit(xData, yData) {
 | ||
|       this.barChart = echarts.init(document.getElementById('barChart'))
 | ||
|       var option = {
 | ||
|         color: ['#2891FF'],
 | ||
|         grid: {
 | ||
|           top: '10%',
 | ||
|           left: '2%',
 | ||
|           right: '2%',
 | ||
|           bottom: '3%',
 | ||
|           containLabel: true
 | ||
|         },
 | ||
|         tooltip: {
 | ||
|           trigger: 'axis',
 | ||
|           axisPointer: {
 | ||
|             type: 'shadow'
 | ||
|           },
 | ||
|         },
 | ||
|        
 | ||
|         xAxis: {
 | ||
|           data: xData,
 | ||
|           axisLabel: {
 | ||
|             show: true, // 是否显示刻度标签,默认显示
 | ||
|             interval: 0, // 坐标轴刻度标签的显示间隔,在类目轴中有效;默认会采用标签不重叠的策略间隔显示标签;可以设置成0强制显示所有标签;如果设置为1,表示『隔一个标签显示一个标签』,如果值为2,表示隔两个标签显示一个标签,以此类推。
 | ||
|             rotate: -60, // 刻度标签旋转的角度,在类目轴的类目标签显示不下的时候可以通过旋转防止标签之间重叠;旋转的角度从-90度到90度
 | ||
|             inside: false, // 刻度标签是否朝内,默认朝外
 | ||
|             margin: 6, // 刻度标签与轴线之间的距离
 | ||
|           },
 | ||
|           silent: false,
 | ||
|           splitLine: {
 | ||
|             show: false
 | ||
|           },
 | ||
|           splitArea: {
 | ||
|             show: false
 | ||
|           }
 | ||
|         },
 | ||
|         yAxis: {
 | ||
|           splitArea: {
 | ||
|             show: false
 | ||
|           }
 | ||
|         },
 | ||
|         series: [
 | ||
|           {
 | ||
|             type: 'bar',
 | ||
|             data: yData,
 | ||
|             barWidth: 20,
 | ||
|             barGap: '250%',
 | ||
|             large: true
 | ||
|           }
 | ||
|         ]
 | ||
|       };
 | ||
|       this.barChart.setOption(option)
 | ||
|     },
 | ||
|     wordChartInit(data) {
 | ||
|       this.wordChart = echarts.init(document.getElementById('wordChart'))
 | ||
|       var option = {
 | ||
|         series: [
 | ||
|           {
 | ||
|             type: "wordCloud",
 | ||
|             sizeRange: [15, 80],
 | ||
|             rotationRange: [0, 0],
 | ||
|             rotationStep: 45,
 | ||
|             gridSize: 8,
 | ||
|             shape: "pentagon",
 | ||
|             width: "100%",
 | ||
|             height: "100%",
 | ||
|             textStyle: {
 | ||
|               normal: {
 | ||
|                 color: function () {
 | ||
|                   return (
 | ||
|                     "rgb(" +
 | ||
|                     [
 | ||
|                       Math.round(Math.random() * 160),
 | ||
|                       Math.round(Math.random() * 160),
 | ||
|                       Math.round(Math.random() * 160),
 | ||
|                     ].join(",") +
 | ||
|                     ")"
 | ||
|                   );
 | ||
|                 },
 | ||
|                 fontFamily: "sans-serif",
 | ||
|                 fontWeight: "normal",
 | ||
|               },
 | ||
|               emphasis: {
 | ||
|                 shadowBlur: 10,
 | ||
|                 shadowColor: "#333",
 | ||
|               },
 | ||
|             },
 | ||
|             data,
 | ||
|           },
 | ||
|         ],
 | ||
|       }
 | ||
|       this.wordChart.setOption(option)
 | ||
|     },
 | ||
|     getDeptList() {
 | ||
|       this.instance.post(`/app/wxcp/wxdepartment/listAll`).then((res) => {
 | ||
|         if (res.code == 0) {
 | ||
|           this.deptOptions = this.toTree(res.data)
 | ||
|         }
 | ||
|       })
 | ||
|     },
 | ||
|     toTree(data) {
 | ||
|       let result = [];
 | ||
|       if (!Array.isArray(data)) {
 | ||
|         return result
 | ||
|       }
 | ||
|       let map = {};
 | ||
|       data.forEach(item => {
 | ||
|         map[item.id] = item;
 | ||
|       });
 | ||
|       data.forEach(item => {
 | ||
|         let parent = map[item.parentid];
 | ||
|         if (parent) {
 | ||
|           (parent.children || (parent.children = [])).push(item);
 | ||
|         } else {
 | ||
|           result.push(item);
 | ||
|         }
 | ||
|       });
 | ||
|       return result;
 | ||
|     },
 | ||
|     totalDeptSelect() {
 | ||
|       this.getTotal()
 | ||
|     },
 | ||
|     deptSelect() {
 | ||
|       this.getStatistics()
 | ||
|     },
 | ||
|     timeChange(index) {
 | ||
|       this.type = index
 | ||
|       if (index == 3) {
 | ||
|         this.dialogDate = true
 | ||
|       } else {
 | ||
|         this.startTime = ''
 | ||
|         this.endTime = ''
 | ||
|         this.getStatistics()
 | ||
|       }
 | ||
|     },
 | ||
|     selectDete() {
 | ||
|       if (!this.timeList || !this.timeList.length) {
 | ||
|         return this.$message.error('请选择自定义时间');
 | ||
|       }
 | ||
|       this.startTime = this.timeList?.[0]
 | ||
|       this.endTime = this.timeList?.[1]
 | ||
|       this.dialogDate = false
 | ||
|       this.getStatistics()
 | ||
|     },
 | ||
|   },
 | ||
| }
 | ||
| </script>
 | ||
| 
 | ||
| <style lang="scss" scoped>
 | ||
| .AppAiStatistics {
 | ||
|   height: 100%;
 | ||
|   box-sizing: border-box;
 | ||
| 
 | ||
|   .shortcut {
 | ||
|     display: inline-block;
 | ||
|     width: 70px;
 | ||
|     height: 32px;
 | ||
|     line-height: 32px;
 | ||
|     border-radius: 2px;
 | ||
|     border: 1px solid #D0D4DC;
 | ||
|     margin-right: 8px;
 | ||
|     text-align: center;
 | ||
|     cursor: pointer;
 | ||
| 
 | ||
|     &.active {
 | ||
|       color: #2266FF;
 | ||
|       border: 1px solid #2266FF;
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   :deep .ai-detail__content--wrapper {
 | ||
|     height: 100%;
 | ||
|   }
 | ||
|   .chart-content {
 | ||
|     // height: calc(100% - 140px);
 | ||
|   }
 | ||
| 
 | ||
|   :deep .ai-table {
 | ||
|     height: calc(100% - 40px);
 | ||
|     overflow-y: scroll;
 | ||
|   }
 | ||
| 
 | ||
|   .chartBox {
 | ||
|     background: #F9F9F9;
 | ||
|     box-shadow: 0px 4px 6px -2px rgba(15, 15, 21, 0.1500);
 | ||
|     border-radius: 4px;
 | ||
|     padding: 16px;
 | ||
|     box-sizing: border-box;
 | ||
|     margin-top: 6px;
 | ||
|     // height: 300px;
 | ||
| 
 | ||
|     .chart {
 | ||
|       width: 100%;
 | ||
|       height: 100%;
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   .card_list {
 | ||
|     display: flex;
 | ||
|     margin-bottom: 4px;
 | ||
| 
 | ||
|     .card {
 | ||
|       flex: 1;
 | ||
|       height: 76px;
 | ||
|       background: #F9F9F9;
 | ||
|       border-radius: 2px;
 | ||
|       margin-right: 20px;
 | ||
|       padding: 8px 24px;
 | ||
|       box-sizing: border-box;
 | ||
| 
 | ||
|       h2 {
 | ||
|         color: #888888;
 | ||
|         font-weight: 600;
 | ||
|         font-size: 16px;
 | ||
|       }
 | ||
| 
 | ||
|       p {
 | ||
|         margin-top: 8px;
 | ||
|         font-size: 24px;
 | ||
|         font-weight: 600;
 | ||
|       }
 | ||
| 
 | ||
|       .color1 {
 | ||
|         color: #2891FF;
 | ||
|       }
 | ||
| 
 | ||
|       .color2 {
 | ||
|         color: #22AA99;
 | ||
|       }
 | ||
| 
 | ||
|       .color3 {
 | ||
|         color: #F8B425;
 | ||
|       }
 | ||
|     }
 | ||
| 
 | ||
|     .card:last-child {
 | ||
|       margin-right: 0;
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
| 
 | ||
|   :deep( .el-dialog__footer ) {
 | ||
|     text-align: center;
 | ||
|   }
 | ||
| 
 | ||
|   :deep( .el-dialog__header ) {
 | ||
|     border-bottom: 1px solid #DDD;
 | ||
|   }
 | ||
| 
 | ||
|   :deep( .ai-detail ) {
 | ||
|     background: #FFF;
 | ||
|   }
 | ||
| }
 | ||
| </style>
 |