Files
dvcp_v2_webapp/packages/xbot/AppAiStatistics/AppAiStatistics.vue
2024-03-26 16:57:18 +08:00

529 lines
15 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>