Skip to content

Commit 48c4ffc

Browse files
feat: Application overview adds user token statistics
1 parent 7db2714 commit 48c4ffc

File tree

7 files changed

+231
-25
lines changed

7 files changed

+231
-25
lines changed
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
<template>
2+
<div :id="id" ref="BarChartRef" :style="{ height: height, width: width }" />
3+
</template>
4+
<script lang="ts" setup>
5+
import { onMounted, nextTick, watch, onBeforeUnmount } from 'vue'
6+
import * as echarts from 'echarts'
7+
import { numberFormat } from '@/utils/common'
8+
const props = defineProps({
9+
id: {
10+
type: String,
11+
default: 'barChartId',
12+
},
13+
width: {
14+
type: String,
15+
default: '100%',
16+
},
17+
height: {
18+
type: String,
19+
default: '200px',
20+
},
21+
option: {
22+
type: Object,
23+
required: true,
24+
}, // option: { title , xData, yData, formatStr }
25+
})
26+
27+
const color = ['rgba(82, 133, 255, 1)', 'rgba(255, 207, 47, 1)']
28+
29+
const areaColor = ['rgba(82, 133, 255, 0.2)', 'rgba(255, 207, 47, 0.2)']
30+
31+
function initChart() {
32+
let myChart = echarts?.getInstanceByDom(document.getElementById(props.id)!)
33+
if (myChart === null || myChart === undefined) {
34+
myChart = echarts.init(document.getElementById(props.id))
35+
}
36+
const series: any = []
37+
if (props.option?.yData?.length) {
38+
props.option?.yData.forEach((item: any, index: number) => {
39+
series.push({
40+
type: 'bar',
41+
barWidth: '20',
42+
itemStyle: {
43+
color: color[index],
44+
},
45+
areaStyle: item.area
46+
? {
47+
color: areaColor[index],
48+
}
49+
: null,
50+
...item,
51+
})
52+
})
53+
}
54+
const option = {
55+
title: {
56+
text: props.option?.title,
57+
textStyle: {
58+
fontSize: '16px',
59+
},
60+
},
61+
tooltip: {
62+
trigger: 'axis',
63+
valueFormatter: (value: any) => numberFormat(value),
64+
},
65+
legend: {
66+
right: 0,
67+
itemWidth: 8,
68+
textStyle: {
69+
color: '#646A73',
70+
},
71+
icon: 'circle',
72+
},
73+
grid: {
74+
left: '1%',
75+
right: '1%',
76+
bottom: '0',
77+
top: '18%',
78+
containLabel: true,
79+
},
80+
xAxis: {
81+
type: 'category',
82+
data: props.option.xData,
83+
},
84+
yAxis: {
85+
type: 'value',
86+
splitLine: {
87+
lineStyle: {
88+
color: '#EFF0F1',
89+
},
90+
},
91+
axisLabel: {
92+
formatter: (value: any) => {
93+
return numberFormat(value)
94+
},
95+
},
96+
},
97+
series: series,
98+
}
99+
100+
// 渲染数据
101+
myChart.setOption(option, true)
102+
}
103+
104+
function changeChartSize() {
105+
echarts.getInstanceByDom(document.getElementById(props.id)!)?.resize()
106+
}
107+
108+
watch(
109+
() => props.option,
110+
(val) => {
111+
if (val) {
112+
nextTick(() => {
113+
initChart()
114+
})
115+
}
116+
},
117+
)
118+
119+
onMounted(() => {
120+
nextTick(() => {
121+
initChart()
122+
window.addEventListener('resize', changeChartSize)
123+
})
124+
})
125+
126+
onBeforeUnmount(() => {
127+
window.removeEventListener('resize', changeChartSize)
128+
})
129+
</script>
130+
<style lang="scss" scoped></style>

ui/src/components/app-charts/components/LineCharts.vue

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template>
2-
<div :id="id" ref="PieChartRef" :style="{ height: height, width: width }" />
2+
<div :id="id" ref="LineChartRef" :style="{ height: height, width: width }" />
33
</template>
44
<script lang="ts" setup>
55
import { onMounted, nextTick, watch, onBeforeUnmount } from 'vue'
@@ -21,7 +21,7 @@ const props = defineProps({
2121
option: {
2222
type: Object,
2323
required: true,
24-
}, // option: { title , data }
24+
}, // option: { title , xData, yData, formatStr }
2525
})
2626
2727
const color = ['rgba(82, 133, 255, 1)', 'rgba(255, 207, 47, 1)']
@@ -37,6 +37,7 @@ function initChart() {
3737
if (props.option?.yData?.length) {
3838
props.option?.yData.forEach((item: any, index: number) => {
3939
series.push({
40+
type: 'line',
4041
itemStyle: {
4142
color: color[index],
4243
},

ui/src/components/app-charts/index.vue

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,23 @@
99
</template>
1010
<script lang="ts" setup>
1111
import line from './components/LineCharts.vue'
12+
import bar from './components/BarCharts.vue'
1213
defineOptions({ name: 'AppCharts' })
1314
defineProps({
1415
type: {
1516
type: String,
16-
default: 'line'
17+
default: 'line',
1718
},
1819
height: {
1920
type: String,
20-
default: '200px'
21+
default: '200px',
2122
},
2223
dataZoom: Boolean,
2324
option: {
2425
type: Object,
25-
required: true
26-
} // { title , xData, yData, formatStr }
26+
required: true,
27+
}, // { title , xData, yData, formatStr }
2728
})
2829
29-
const typeComponentMap = { line } as any
30+
const typeComponentMap = { line, bar } as any
3031
</script>

ui/src/locales/lang/en-US/views/application-overview.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ export default {
9797
userSatisfaction: 'User Feedback Metrics',
9898
approval: 'Like',
9999
disapproval: 'Dislike',
100+
tokenUsage: 'User used Tokens',
101+
topQuestions: 'Number of user question',
100102
},
101103
},
102104
}

ui/src/locales/lang/zh-CN/views/application-overview.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ export default {
9797
userSatisfaction: '用户满意度',
9898
approval: '赞同',
9999
disapproval: '反对',
100+
tokenUsage: '用户消耗 Tokens',
101+
topQuestions: '用户提问次数',
102+
100103
},
101104
},
102105
}

ui/src/locales/lang/zh-Hant/views/application-overview.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ export default {
9797
userSatisfaction: '用戶滿意度',
9898
approval: '贊同',
9999
disapproval: '反對',
100+
tokenUsage: '用戶消耗 Tokens',
101+
topQuestions: '用戶提問次數',
100102
},
101103
},
102104
}

ui/src/views/application-overview/component/StatisticsCharts.vue

Lines changed: 85 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@
1313
<el-card shadow="never">
1414
<div class="flex align-center ml-8 mr-8">
1515
<el-avatar :size="40" shape="square" :style="{ background: item.background }">
16-
<appIcon :iconName="item.icon" :style="{ fontSize: '24px', color: item.color }"/>
16+
<appIcon :iconName="item.icon" :style="{ fontSize: '24px', color: item.color }" />
1717
</el-avatar>
1818
<div class="ml-12">
1919
<p class="color-secondary lighter mb-4">{{ item.name }}</p>
2020
<div v-if="item.id !== 'starCharts'" class="flex align-baseline">
2121
<h2>{{ numberFormat(item.sum?.[0]) }}</h2>
2222
<span v-if="item.sum.length > 1" class="ml-12" style="color: #f54a45"
23-
>+{{ numberFormat(item.sum?.[1]) }}</span
23+
>+{{ numberFormat(item.sum?.[1]) }}</span
2424
>
2525
</div>
2626
<div v-else class="flex align-center mr-8">
@@ -33,8 +33,6 @@
3333
</div>
3434
</el-card>
3535
</el-col>
36-
</el-row>
37-
<el-row :gutter="16">
3836
<el-col
3937
:xs="24"
4038
:sm="24"
@@ -47,18 +45,54 @@
4745
>
4846
<el-card shadow="never">
4947
<div class="p-8">
50-
<AppCharts height="316px" :id="item.id" type="line" :option="item.option"/>
48+
<AppCharts height="316px" :id="item.id" type="line" :option="item.option" />
49+
</div>
50+
</el-card>
51+
</el-col>
52+
53+
<el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12" class="mb-16">
54+
<el-card shadow="never" class="StatisticsCharts-card">
55+
<el-select v-model="tokenUsageCount" class="top-select">
56+
<el-option
57+
v-for="item in topOptions"
58+
:key="item.value"
59+
:label="item.label"
60+
:value="item.value"
61+
/>
62+
</el-select>
63+
<div class="p-8">
64+
<AppCharts height="316px" id="tokenUsageCharts" type="bar" :option="tokenUsageOption" />
65+
</div>
66+
</el-card>
67+
</el-col>
68+
<el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12" class="mb-16">
69+
<el-card shadow="never" class="StatisticsCharts-card">
70+
<el-select v-model="topQuestionsCount" class="top-select">
71+
<el-option
72+
v-for="item in topOptions"
73+
:key="item.value"
74+
:label="item.label"
75+
:value="item.value"
76+
/>
77+
</el-select>
78+
<div class="p-8">
79+
<AppCharts
80+
height="316px"
81+
id="topQuestionsCharts"
82+
type="bar"
83+
:option="topQuestionsOption"
84+
/>
5185
</div>
5286
</el-card>
5387
</el-col>
5488
</el-row>
5589
</template>
5690
<script setup lang="ts">
57-
import {ref, computed, onMounted} from 'vue'
91+
import { ref, computed, onMounted } from 'vue'
5892
import AppCharts from '@/components/app-charts/index.vue'
59-
import {getAttrsArray, getSum} from '@/utils/array'
60-
import {numberFormat} from '@/utils/common'
61-
import {t} from '@/locales'
93+
import { getAttrsArray, getSum } from '@/utils/array'
94+
import { numberFormat } from '@/utils/common'
95+
import { t } from '@/locales'
6296
6397
const props = defineProps({
6498
data: {
@@ -70,10 +104,12 @@ const props = defineProps({
70104
default: () => [],
71105
},
72106
topQuestions: {
73-
type: Array,
107+
type: Array,
74108
default: () => [],
75-
}
109+
},
76110
})
111+
112+
77113
const statisticsType = computed(() => [
78114
{
79115
id: 'customerCharts',
@@ -91,13 +127,11 @@ const statisticsType = computed(() => [
91127
yData: [
92128
{
93129
name: t('views.applicationOverview.monitor.charts.customerTotal'),
94-
type: 'line',
95130
area: true,
96131
data: getAttrsArray(props.data, 'customer_num'),
97132
},
98133
{
99134
name: t('views.applicationOverview.monitor.charts.customerNew'),
100-
type: 'line',
101135
area: true,
102136
data: getAttrsArray(props.data, 'customer_added_count'),
103137
},
@@ -116,7 +150,6 @@ const statisticsType = computed(() => [
116150
xData: getAttrsArray(props.data, 'day'),
117151
yData: [
118152
{
119-
type: 'line',
120153
data: getAttrsArray(props.data, 'chat_record_count'),
121154
},
122155
],
@@ -134,7 +167,6 @@ const statisticsType = computed(() => [
134167
xData: getAttrsArray(props.data, 'day'),
135168
yData: [
136169
{
137-
type: 'line',
138170
data: getAttrsArray(props.data, 'tokens_num'),
139171
},
140172
],
@@ -156,17 +188,52 @@ const statisticsType = computed(() => [
156188
yData: [
157189
{
158190
name: t('views.applicationOverview.monitor.charts.approval'),
159-
type: 'line',
160191
data: getAttrsArray(props.data, 'star_num'),
161192
},
162193
{
163194
name: t('views.applicationOverview.monitor.charts.disapproval'),
164-
type: 'line',
165195
data: getAttrsArray(props.data, 'trample_num'),
166196
},
167197
],
168198
},
169199
},
170200
])
201+
202+
const topOptions = [{ label: 'TOP 10', value: 10 }]
203+
const tokenUsageCount = ref(10)
204+
const topQuestionsCount = ref(10)
205+
const tokenUsageOption = computed(() => {
206+
return {
207+
title: t('views.applicationOverview.monitor.charts.tokenUsage'),
208+
xData: getAttrsArray(props.tokenUsage?.slice(0, tokenUsageCount.value), 'username'),
209+
yData: [
210+
{
211+
data: getAttrsArray(props.tokenUsage?.slice(0, tokenUsageCount.value), 'token_usage'),
212+
},
213+
],
214+
}
215+
})
216+
const topQuestionsOption = computed(() => {
217+
return {
218+
title: t('views.applicationOverview.monitor.charts.topQuestions'),
219+
xData: getAttrsArray(props.topQuestions?.slice(0, topQuestionsCount.value), 'username'),
220+
yData: [
221+
{
222+
data: getAttrsArray(props.topQuestions?.slice(0, topQuestionsCount.value), 'chat_record_count'),
223+
},
224+
],
225+
}
226+
})
171227
</script>
172-
<style lang="scss" scoped></style>
228+
<style lang="scss" scoped>
229+
.StatisticsCharts-card {
230+
position: relative;
231+
.top-select {
232+
position: absolute;
233+
top: 16px;
234+
right: 16px;
235+
z-index: 10;
236+
width: 100px;
237+
}
238+
}
239+
</style>

0 commit comments

Comments
 (0)