mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-26 01:33:05 +00:00
feat: 应用概览增加监控统计
This commit is contained in:
parent
05d01144f9
commit
e382bb6bba
|
|
@ -14,6 +14,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.28.0",
|
||||
"echarts": "^5.5.0",
|
||||
"element-plus": "^2.5.6",
|
||||
"install": "^0.13.0",
|
||||
"lodash": "^4.17.21",
|
||||
|
|
@ -29,6 +30,7 @@
|
|||
"md-editor-v3": "^4.12.1",
|
||||
"medium-zoom": "^1.1.0",
|
||||
"mitt": "^3.0.0",
|
||||
"moment": "^2.30.1",
|
||||
"npm": "^10.2.4",
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^2.1.6",
|
||||
|
|
|
|||
|
|
@ -55,9 +55,22 @@ const putAPIKey: (
|
|||
return put(`${prefix}/${applicaiton_id}/api_key/${api_key_id}`, data, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计
|
||||
* @param 参数 applicaiton_id, data
|
||||
*/
|
||||
const getStatistics: (
|
||||
applicaiton_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>
|
||||
) => Promise<Result<any>> = (applicaiton_id, data, loading) => {
|
||||
return get(`${prefix}/${applicaiton_id}/statistics/chat_record_aggregate_trend`, data, loading)
|
||||
}
|
||||
|
||||
export default {
|
||||
getAPIKey,
|
||||
postAPIKey,
|
||||
delAPIKey,
|
||||
putAPIKey
|
||||
putAPIKey,
|
||||
getStatistics
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,128 @@
|
|||
<template>
|
||||
<div :id="id" ref="PieChartRef" :style="{ height: height, width: width }" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, nextTick, watch, onBeforeUnmount } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
const props = defineProps({
|
||||
id: {
|
||||
type: String,
|
||||
default: 'lineChartId'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '200px'
|
||||
},
|
||||
option: {
|
||||
type: Object,
|
||||
required: true
|
||||
} // option: { title , data }
|
||||
})
|
||||
|
||||
const color = ['rgba(82, 133, 255, 1)', 'rgba(255, 207, 47, 1)']
|
||||
|
||||
const areaColor = ['rgba(82, 133, 255, 0.2)', 'rgba(255, 207, 47, 0.2)']
|
||||
|
||||
function initChart() {
|
||||
let myChart = echarts?.getInstanceByDom(document.getElementById(props.id)!)
|
||||
if (myChart === null || myChart === undefined) {
|
||||
myChart = echarts.init(document.getElementById(props.id))
|
||||
}
|
||||
const series: any = []
|
||||
if (props.option?.yDatas?.length) {
|
||||
props.option?.yDatas.forEach((item: any, index: number) => {
|
||||
series.push({
|
||||
itemStyle: {
|
||||
color: color[index]
|
||||
},
|
||||
areaStyle: item.area
|
||||
? {
|
||||
color: areaColor[index]
|
||||
}
|
||||
: null,
|
||||
...item
|
||||
})
|
||||
})
|
||||
}
|
||||
const option = {
|
||||
title: {
|
||||
text: props.option?.title,
|
||||
textStyle: {
|
||||
fontSize: '16px'
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
label: {
|
||||
backgroundColor: '#6a7985'
|
||||
}
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
right: 0,
|
||||
itemWidth: 8,
|
||||
textStyle: {
|
||||
color: '#646A73'
|
||||
},
|
||||
icon: 'circle'
|
||||
},
|
||||
grid: {
|
||||
left: '1%',
|
||||
right: '1%',
|
||||
bottom: '0',
|
||||
top: '18%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: props.option.xDatas
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: '#EFF0F1'
|
||||
}
|
||||
}
|
||||
},
|
||||
series: series
|
||||
}
|
||||
|
||||
// 渲染数据
|
||||
myChart.setOption(option, true)
|
||||
}
|
||||
|
||||
function changeChartSize() {
|
||||
echarts.getInstanceByDom(document.getElementById(props.id)!)?.resize()
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.option,
|
||||
(val) => {
|
||||
if (val) {
|
||||
nextTick(() => {
|
||||
initChart()
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
initChart()
|
||||
window.addEventListener('resize', changeChartSize)
|
||||
})
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
echarts.getInstanceByDom(document.getElementById(props.id)!)?.dispose()
|
||||
window.removeEventListener('resize', changeChartSize)
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<template>
|
||||
<component
|
||||
:is="typeComponentMap[type]"
|
||||
:height="height"
|
||||
:option="option"
|
||||
:dataZoom="dataZoom"
|
||||
class="v-charts"
|
||||
/>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import line from './components/LineCharts.vue'
|
||||
defineOptions({ name: 'AppCharts' })
|
||||
defineProps({
|
||||
type: {
|
||||
type: String,
|
||||
default: 'line'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '200px'
|
||||
},
|
||||
dataZoom: Boolean,
|
||||
option: {
|
||||
type: Object,
|
||||
required: true
|
||||
} // { title , xDatas, yDatas, formatStr }
|
||||
})
|
||||
|
||||
const typeComponentMap = { line } as any
|
||||
</script>
|
||||
|
|
@ -640,5 +640,93 @@ export const iconMap: any = {
|
|||
)
|
||||
])
|
||||
}
|
||||
},
|
||||
'app-user': {
|
||||
iconReader: () => {
|
||||
return h('i', [
|
||||
h(
|
||||
'svg',
|
||||
{
|
||||
style: { height: '100%', width: '100%' },
|
||||
viewBox: '0 0 24 24',
|
||||
version: '1.1',
|
||||
xmlns: 'http://www.w3.org/2000/svg'
|
||||
},
|
||||
[
|
||||
h('path', {
|
||||
d: 'M15 13H9C6.23858 13 3 14.9314 3 18.4V21.1C3 21.597 3.44772 22 4 22H20C20.5523 22 21 21.597 21 21.1V18.4C21 14.9285 17.7614 13 15 13Z',
|
||||
fill: 'currentColor'
|
||||
}),
|
||||
h('path', {
|
||||
d: 'M7 6.99997C7 9.76139 9.23858 12 12 12C14.7614 12 17 9.76139 17 6.99997C17 4.23855 14.7614 1.99997 12 1.99997C9.23858 1.99997 7 4.23855 7 6.99997Z',
|
||||
fill: 'currentColor'
|
||||
})
|
||||
]
|
||||
)
|
||||
])
|
||||
}
|
||||
},
|
||||
'app-question': {
|
||||
iconReader: () => {
|
||||
return h('i', [
|
||||
h(
|
||||
'svg',
|
||||
{
|
||||
style: { height: '100%', width: '100%' },
|
||||
viewBox: '0 0 24 24',
|
||||
version: '1.1',
|
||||
xmlns: 'http://www.w3.org/2000/svg'
|
||||
},
|
||||
[
|
||||
h('path', {
|
||||
d: 'M12.7071 22.2009L17 18.5111H21.5C22.0523 18.5111 22.5 18.0539 22.5 17.4899V2.52112C22.5 1.95715 22.0523 1.49997 21.5 1.49997H2C1.44772 1.49997 1 1.95715 1 2.52112V17.4899C1 18.0539 1.44772 18.5111 2 18.5111H7L11.2929 22.2009C11.6834 22.5997 12.3166 22.5997 12.7071 22.2009ZM6.5 8.49997H7.5C8.05228 8.49997 8.5 8.94768 8.5 9.49997V10.5C8.5 11.0523 8.05228 11.5 7.5 11.5H6.5C5.94772 11.5 5.5 11.0523 5.5 10.5V9.49997C5.5 8.94768 5.94772 8.49997 6.5 8.49997ZM10.5 9.49997C10.5 8.94768 10.9477 8.49997 11.5 8.49997H12.5C13.0523 8.49997 13.5 8.94768 13.5 9.49997V10.5C13.5 11.0523 13.0523 11.5 12.5 11.5H11.5C10.9477 11.5 10.5 11.0523 10.5 10.5V9.49997ZM16.5 8.49997H17.5C18.0523 8.49997 18.5 8.94768 18.5 9.49997V10.5C18.5 11.0523 18.0523 11.5 17.5 11.5H16.5C15.9477 11.5 15.5 11.0523 15.5 10.5V9.49997C15.5 8.94768 15.9477 8.49997 16.5 8.49997Z',
|
||||
fill: 'currentColor'
|
||||
})
|
||||
]
|
||||
)
|
||||
])
|
||||
}
|
||||
},
|
||||
'app-tokens': {
|
||||
iconReader: () => {
|
||||
return h('i', [
|
||||
h(
|
||||
'svg',
|
||||
{
|
||||
style: { height: '100%', width: '100%' },
|
||||
viewBox: '0 0 24 24',
|
||||
version: '1.1',
|
||||
xmlns: 'http://www.w3.org/2000/svg'
|
||||
},
|
||||
[
|
||||
h('path', {
|
||||
d: 'M15.6 2.39996C12.288 2.39996 9.60002 5.08796 9.60002 8.39996C9.60002 9.11996 9.74402 9.79196 9.97202 10.428L2.47325 17.9267C2.42636 17.9736 2.40002 18.0372 2.40002 18.1035V21.1C2.40002 21.3761 2.62388 21.6 2.90002 21.6H4.30002C4.57617 21.6 4.80002 21.3761 4.80002 21.1V20.4H6.70003C6.97617 20.4 7.20002 20.1761 7.20002 19.9V18H8.40002L10.8 15.6H12L13.572 14.028C14.208 14.256 14.88 14.4 15.6 14.4C18.912 14.4 21.6 11.712 21.6 8.39996C21.6 5.08796 18.912 2.39996 15.6 2.39996ZM17.4 8.39996C16.404 8.39996 15.6 7.59596 15.6 6.59996C15.6 5.60396 16.404 4.79996 17.4 4.79996C18.396 4.79996 19.2 5.60396 19.2 6.59996C19.2 7.59596 18.396 8.39996 17.4 8.39996Z',
|
||||
fill: 'currentColor'
|
||||
})
|
||||
]
|
||||
)
|
||||
])
|
||||
}
|
||||
},
|
||||
'app-user-stars': {
|
||||
iconReader: () => {
|
||||
return h('i', [
|
||||
h(
|
||||
'svg',
|
||||
{
|
||||
style: { height: '100%', width: '100%' },
|
||||
viewBox: '0 0 24 24',
|
||||
version: '1.1',
|
||||
xmlns: 'http://www.w3.org/2000/svg'
|
||||
},
|
||||
[
|
||||
h('path', {
|
||||
d: 'M12 23C18.0751 23 23 18.0751 23 12C23 5.92484 18.0751 0.999969 12 0.999969C5.92487 0.999969 1 5.92484 1 12C1 18.0751 5.92487 23 12 23ZM8.5 10.5C7.67157 10.5 7 9.8284 7 8.99997C7 8.17154 7.67157 7.49997 8.5 7.49997C9.32843 7.49997 10 8.17154 10 8.99997C10 9.8284 9.32843 10.5 8.5 10.5ZM17 8.99997C17 9.8284 16.3284 10.5 15.5 10.5C14.6716 10.5 14 9.8284 14 8.99997C14 8.17154 14.6716 7.49997 15.5 7.49997C16.3284 7.49997 17 8.17154 17 8.99997ZM16.9779 13.4994C16.7521 16.0264 14.8169 18 12 18C9.18312 18 7.24789 16.0264 7.02213 13.4994C6.99756 13.2244 7.22386 13 7.5 13H16.5C16.7761 13 17.0024 13.2244 16.9779 13.4994Z',
|
||||
fill: 'currentColor'
|
||||
})
|
||||
]
|
||||
)
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -105,6 +105,9 @@ h4 {
|
|||
.h-full {
|
||||
height: 100%;
|
||||
}
|
||||
.w-120 {
|
||||
width: 120px;
|
||||
}
|
||||
.w-240 {
|
||||
width: 240px;
|
||||
}
|
||||
|
|
@ -206,6 +209,10 @@ h4 {
|
|||
align-items: center;
|
||||
}
|
||||
|
||||
.align-baseline {
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.text-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
|
@ -424,9 +431,13 @@ h4 {
|
|||
color: var(--el-color-info);
|
||||
}
|
||||
|
||||
.color-secondary {
|
||||
color: var(--app-text-color-secondary);
|
||||
}
|
||||
|
||||
.app-warning-icon {
|
||||
font-size: 16px;
|
||||
color: #646a73;
|
||||
color: var(--app-text-color-secondary);
|
||||
}
|
||||
|
||||
.dotting {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,13 @@
|
|||
import moment from 'moment'
|
||||
|
||||
// 当天日期 YYYY-MM-DD
|
||||
export const nowDate = moment().format('YYYY-MM-DD')
|
||||
|
||||
// 当前时间的前n天
|
||||
export function beforeDay(n) {
|
||||
return moment().subtract(n, 'days').format('YYYY-MM-DD')
|
||||
}
|
||||
|
||||
const getCheckDate = (timestamp: any) => {
|
||||
if (!timestamp) return false
|
||||
const dt = new Date(timestamp)
|
||||
|
|
|
|||
|
|
@ -64,3 +64,15 @@ export function isAllPropertiesEmpty(obj: object) {
|
|||
value === null || typeof value === 'undefined' || (typeof value === 'string' && !value)
|
||||
)
|
||||
}
|
||||
|
||||
// 数组对象中某一属性值的集合
|
||||
export function getAttrsArray(array, attr) {
|
||||
return array.map((item) => {
|
||||
return item[attr]
|
||||
})
|
||||
}
|
||||
|
||||
// 求和
|
||||
export function getSum(array) {
|
||||
return array.reduce((totol, item) => totol + item, 0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,161 @@
|
|||
<template>
|
||||
<el-row :gutter="16">
|
||||
<el-col
|
||||
:xs="12"
|
||||
:sm="12"
|
||||
:md="12"
|
||||
:lg="6"
|
||||
:xl="6"
|
||||
v-for="(item, index) in statisticsType"
|
||||
:key="index"
|
||||
class="mb-16"
|
||||
>
|
||||
<el-card shadow="never">
|
||||
<div class="flex align-center ml-8 mr-8">
|
||||
<el-avatar :size="40" shape="square" :style="{ background: item.background }">
|
||||
<appIcon :iconName="item.icon" :style="{ fontSize: '24px', color: item.color }" />
|
||||
</el-avatar>
|
||||
<div class="ml-12">
|
||||
<p class="color-secondary lighter mb-4">{{ item.name }}</p>
|
||||
<div v-if="item.id !== 'starCharts'" class="flex align-baseline">
|
||||
<h2>{{ numberFormat(item.sum?.[0]) }}</h2>
|
||||
<span v-if="item.sum.length > 1" class="ml-12" style="color: #f54a45"
|
||||
>+{{ numberFormat(item.sum?.[1]) }}</span
|
||||
>
|
||||
</div>
|
||||
<div v-else class="flex align-center mr-8">
|
||||
<AppIcon iconName="app-like-color"></AppIcon>
|
||||
<h2 class="ml-4">{{ item.sum?.[0] }}</h2>
|
||||
<AppIcon class="ml-12" iconName="app-oppose-color"></AppIcon>
|
||||
<h2 class="ml-4">{{ item.sum?.[1] }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col
|
||||
:xs="24"
|
||||
:sm="24"
|
||||
:md="24"
|
||||
:lg="12"
|
||||
:xl="12"
|
||||
v-for="(item, index) in statisticsType"
|
||||
:key="index"
|
||||
class="mb-16"
|
||||
>
|
||||
<el-card shadow="never">
|
||||
<div class="p-8">
|
||||
<AppCharts height="316px" :id="item.id" type="line" :option="item.option" />
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import AppCharts from '@/components/app-charts/index.vue'
|
||||
import { getAttrsArray, getSum, numberFormat } from '@/utils/utils'
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
const statisticsType = computed(() => [
|
||||
{
|
||||
id: 'customerCharts',
|
||||
name: '用户总数',
|
||||
icon: 'app-user',
|
||||
background: '#EBF1FF',
|
||||
color: '#3370FF',
|
||||
sum: [
|
||||
getSum(getAttrsArray(props.data, 'customer_num') || 0),
|
||||
getSum(getAttrsArray(props.data, 'customer_added_count') || 0)
|
||||
],
|
||||
option: {
|
||||
title: '用户总数',
|
||||
xDatas: getAttrsArray(props.data, 'day'),
|
||||
yDatas: [
|
||||
{
|
||||
name: '用户总数',
|
||||
type: 'line',
|
||||
area: true,
|
||||
data: getAttrsArray(props.data, 'customer_num')
|
||||
},
|
||||
{
|
||||
name: '用户新增数',
|
||||
type: 'line',
|
||||
area: true,
|
||||
data: getAttrsArray(props.data, 'customer_added_count')
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'chatRecordCharts',
|
||||
name: '提问次数',
|
||||
icon: 'app-question',
|
||||
background: '#FFF3E5',
|
||||
color: '#FF8800',
|
||||
sum: [getSum(getAttrsArray(props.data, 'chat_record_count') || 0)],
|
||||
option: {
|
||||
title: '提问次数',
|
||||
xDatas: getAttrsArray(props.data, 'day'),
|
||||
yDatas: [
|
||||
{
|
||||
type: 'line',
|
||||
data: getAttrsArray(props.data, 'chat_record_count')
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'tokensCharts',
|
||||
name: 'Tokens 总数',
|
||||
icon: 'app-tokens',
|
||||
background: '#E5FBF8',
|
||||
color: '#00D6B9',
|
||||
sum: [getSum(getAttrsArray(props.data, 'tokens_num') || 0)],
|
||||
option: {
|
||||
title: 'Tokens 总数',
|
||||
xDatas: getAttrsArray(props.data, 'day'),
|
||||
yDatas: [
|
||||
{
|
||||
type: 'line',
|
||||
data: getAttrsArray(props.data, 'tokens_num')
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'starCharts',
|
||||
name: '用户满意度',
|
||||
icon: 'app-user-stars',
|
||||
background: '#FEEDEC',
|
||||
color: '#F54A45',
|
||||
sum: [
|
||||
getSum(getAttrsArray(props.data, 'star_num') || 0),
|
||||
getSum(getAttrsArray(props.data, 'trample_num') || 0)
|
||||
],
|
||||
option: {
|
||||
title: '用户满意度',
|
||||
xDatas: getAttrsArray(props.data, 'day'),
|
||||
yDatas: [
|
||||
{
|
||||
name: '赞同',
|
||||
type: 'line',
|
||||
data: getAttrsArray(props.data, 'star_num')
|
||||
},
|
||||
{
|
||||
name: '反对',
|
||||
type: 'line',
|
||||
data: getAttrsArray(props.data, 'trample_num')
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
])
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
@ -1,78 +1,104 @@
|
|||
<template>
|
||||
<LayoutContainer header="概览">
|
||||
<div class="main-calc-height p-24" style="min-width: 600px">
|
||||
<h4 class="title-decoration-1 mb-16">应用信息</h4>
|
||||
<el-card shadow="never" class="overview-card" v-loading="loading">
|
||||
<div class="title flex align-center">
|
||||
<AppAvatar
|
||||
v-if="detail?.name"
|
||||
:name="detail?.name"
|
||||
pinyinColor
|
||||
class="mr-12"
|
||||
shape="square"
|
||||
:size="32"
|
||||
<el-scrollbar>
|
||||
<div class="main-calc-height p-24">
|
||||
<h4 class="title-decoration-1 mb-16">应用信息</h4>
|
||||
<el-card shadow="never" class="overview-card" v-loading="loading">
|
||||
<div class="title flex align-center">
|
||||
<AppAvatar
|
||||
v-if="detail?.name"
|
||||
:name="detail?.name"
|
||||
pinyinColor
|
||||
class="mr-12"
|
||||
shape="square"
|
||||
:size="32"
|
||||
/>
|
||||
<h4>{{ detail?.name }}</h4>
|
||||
</div>
|
||||
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="12" class="mt-16">
|
||||
<div class="flex">
|
||||
<el-text type="info">公开访问链接</el-text>
|
||||
<el-switch
|
||||
v-model="accessToken.is_active"
|
||||
class="ml-8"
|
||||
size="small"
|
||||
inline-prompt
|
||||
active-text="开"
|
||||
inactive-text="关"
|
||||
@change="changeState($event)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 mb-16 url-height">
|
||||
<span class="vertical-middle lighter break-all">
|
||||
{{ shareUrl }}
|
||||
</span>
|
||||
|
||||
<el-button type="primary" text @click="copyClick(shareUrl)">
|
||||
<AppIcon iconName="app-copy"></AppIcon>
|
||||
</el-button>
|
||||
<el-button @click="refreshAccessToken" type="primary" text style="margin-left: 1px">
|
||||
<el-icon><RefreshRight /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
<div>
|
||||
<el-button :disabled="!accessToken?.is_active" type="primary">
|
||||
<a v-if="accessToken?.is_active" :href="shareUrl" target="_blank"> 演示 </a>
|
||||
<span v-else>演示</span>
|
||||
</el-button>
|
||||
<el-button :disabled="!accessToken?.is_active" @click="openDialog">
|
||||
嵌入第三方
|
||||
</el-button>
|
||||
<el-button @click="openLimitDialog"> 访问限制 </el-button>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mt-16">
|
||||
<div class="flex">
|
||||
<el-text type="info">API访问凭据</el-text>
|
||||
</div>
|
||||
<div class="mt-4 mb-16 url-height">
|
||||
<span class="vertical-middle lighter break-all">
|
||||
{{ apiUrl }}
|
||||
</span>
|
||||
|
||||
<el-button type="primary" text @click="copyClick(apiUrl)">
|
||||
<AppIcon iconName="app-copy"></AppIcon>
|
||||
</el-button>
|
||||
</div>
|
||||
<div>
|
||||
<el-button @click="openAPIKeyDialog"> API Key </el-button>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<h4 class="title-decoration-1 mt-16 mb-16">监控统计</h4>
|
||||
<div class="mb-16">
|
||||
<el-select v-model="history_day" class="mr-12 w-120" @change="changeDayHandle">
|
||||
<el-option
|
||||
v-for="item in dayOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-date-picker
|
||||
v-if="history_day === 'other'"
|
||||
v-model="daterangeValue"
|
||||
type="daterange"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
@change="changeDayRangeHandle"
|
||||
/>
|
||||
<h4>{{ detail?.name }}</h4>
|
||||
</div>
|
||||
|
||||
<el-row :gutter="12">
|
||||
<el-col :span="12" class="mt-16">
|
||||
<div class="flex">
|
||||
<el-text type="info">公开访问链接</el-text>
|
||||
<el-switch
|
||||
v-model="accessToken.is_active"
|
||||
class="ml-8"
|
||||
size="small"
|
||||
inline-prompt
|
||||
active-text="开"
|
||||
inactive-text="关"
|
||||
@change="changeState($event)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 mb-16 url-height">
|
||||
<span class="vertical-middle lighter break-all">
|
||||
{{ shareUrl }}
|
||||
</span>
|
||||
|
||||
<el-button type="primary" text @click="copyClick(shareUrl)">
|
||||
<AppIcon iconName="app-copy"></AppIcon>
|
||||
</el-button>
|
||||
<el-button @click="refreshAccessToken" type="primary" text style="margin-left: 1px">
|
||||
<el-icon><RefreshRight /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
<div>
|
||||
<el-button :disabled="!accessToken?.is_active" type="primary">
|
||||
<a v-if="accessToken?.is_active" :href="shareUrl" target="_blank"> 演示 </a>
|
||||
<span v-else>演示</span>
|
||||
</el-button>
|
||||
<el-button :disabled="!accessToken?.is_active" @click="openDialog">
|
||||
嵌入第三方
|
||||
</el-button>
|
||||
<el-button @click="openLimitDialog"> 访问限制 </el-button>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mt-16">
|
||||
<div class="flex">
|
||||
<el-text type="info">API访问凭据</el-text>
|
||||
</div>
|
||||
<div class="mt-4 mb-16 url-height">
|
||||
<span class="vertical-middle lighter break-all">
|
||||
{{ apiUrl }}
|
||||
</span>
|
||||
|
||||
<el-button type="primary" text @click="copyClick(apiUrl)">
|
||||
<AppIcon iconName="app-copy"></AppIcon>
|
||||
</el-button>
|
||||
</div>
|
||||
<div>
|
||||
<el-button @click="openAPIKeyDialog"> API Key </el-button>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</div>
|
||||
<div v-loading="statisticsLoading">
|
||||
<StatisticsCharts :data="statisticsData" />
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
<EmbedDialog ref="EmbedDialogRef" />
|
||||
<APIKeyDialog ref="APIKeyDialogRef" />
|
||||
<LimitDialog ref="LimitDialogRef" @refresh="refresh" />
|
||||
|
|
@ -80,16 +106,20 @@
|
|||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { useRoute } from 'vue-router'
|
||||
import EmbedDialog from './component/EmbedDialog.vue'
|
||||
import APIKeyDialog from './component/APIKeyDialog.vue'
|
||||
import LimitDialog from './component/LimitDialog.vue'
|
||||
import StatisticsCharts from './component/StatisticsCharts.vue'
|
||||
import applicationApi from '@/api/application'
|
||||
import overviewApi from '@/api/application-overview'
|
||||
import { nowDate, beforeDay } from '@/utils/time'
|
||||
|
||||
import { MsgSuccess, MsgConfirm } from '@/utils/message'
|
||||
|
||||
import { copyClick } from '@/utils/clipboard'
|
||||
import useStore from '@/stores'
|
||||
const { application } = useStore()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const {
|
||||
params: { id }
|
||||
|
|
@ -108,6 +138,63 @@ const loading = ref(false)
|
|||
|
||||
const shareUrl = computed(() => application.location + accessToken.value.access_token)
|
||||
|
||||
const dayOptions = [
|
||||
{
|
||||
value: 7,
|
||||
label: '过去7天'
|
||||
},
|
||||
{
|
||||
value: 30,
|
||||
label: '过去30天'
|
||||
},
|
||||
{
|
||||
value: 90,
|
||||
label: '过去90天'
|
||||
},
|
||||
{
|
||||
value: 183,
|
||||
label: '过去半年'
|
||||
},
|
||||
{
|
||||
value: 'other',
|
||||
label: '自定义'
|
||||
}
|
||||
]
|
||||
|
||||
const history_day = ref<number | string>(7)
|
||||
|
||||
// 日期组件时间
|
||||
const daterangeValue = ref('')
|
||||
|
||||
// 提交日期时间
|
||||
const daterange = ref({
|
||||
start_time: '',
|
||||
end_time: ''
|
||||
})
|
||||
|
||||
const statisticsLoading = ref(false)
|
||||
const statisticsData = ref([])
|
||||
|
||||
function changeDayHandle(val: number | string) {
|
||||
if (val !== 'other') {
|
||||
daterange.value.start_time = beforeDay(val)
|
||||
daterange.value.end_time = nowDate
|
||||
getAppStatistics()
|
||||
}
|
||||
}
|
||||
|
||||
function changeDayRangeHandle(val: string) {
|
||||
daterange.value.start_time = val[0]
|
||||
daterange.value.end_time = val[1]
|
||||
getAppStatistics()
|
||||
}
|
||||
|
||||
function getAppStatistics() {
|
||||
overviewApi.getStatistics(id, daterange.value, statisticsLoading).then((res: any) => {
|
||||
statisticsData.value = res.data
|
||||
})
|
||||
}
|
||||
|
||||
function refreshAccessToken() {
|
||||
const obj = {
|
||||
access_token_reset: true
|
||||
|
|
@ -158,6 +245,7 @@ function refresh() {
|
|||
onMounted(() => {
|
||||
getDetail()
|
||||
getAccessToken()
|
||||
changeDayHandle(history_day.value)
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
|
@ -168,8 +256,5 @@ onMounted(() => {
|
|||
right: 16px;
|
||||
top: 21px;
|
||||
}
|
||||
.url-height {
|
||||
// min-height: 50px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Reference in New Issue