数据统计页面接口对接
This commit is contained in:
@@ -31,3 +31,19 @@ export const ApiContactsExport = (data: any) => {
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取总览统计数据
|
||||
export const ApiStats = (data: any) => {
|
||||
return request({
|
||||
url: `/api/stats?period=${data.period}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取总览统计数据
|
||||
export const ApiStatsDaily = (data: any) => {
|
||||
return request({
|
||||
url: `/api/stats/daily?days=${data.days}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -324,6 +324,25 @@ export const constantRoutes: RouteRecordRaw[] = [
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/data',
|
||||
name: 'Data',
|
||||
component: Layout,
|
||||
meta: {
|
||||
title: '数据统计',
|
||||
icon: 'setting'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'dataStatistics',
|
||||
name: 'DataStatistics',
|
||||
component: () => import('@/views/DataStatistics/index.vue'),
|
||||
meta: {
|
||||
title: '数据统计'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
// 注册平台登记
|
||||
// {
|
||||
|
||||
@@ -187,11 +187,19 @@ export function deepCloneByJSON(obj: any) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 ISO 8601 时间格式转换为年月日时分秒格式
|
||||
* 将 ISO 8601 时间格式转换为指定格式
|
||||
* @param isoString ISO 8601 格式的时间字符串,如 "2026-02-28T14:10:51.966269"
|
||||
* @returns 格式化后的时间字符串,如 "2026-02-28 14:10:51"
|
||||
* @param format 格式化模板,默认 'YYYY-MM-DD HH:mm:ss'
|
||||
* @returns 格式化后的时间字符串
|
||||
*
|
||||
* @example
|
||||
* formatISOToDateTime('2026-02-28T14:10:51.966269') // '2026-02-28 14:10:51'
|
||||
* formatISOToDateTime('2026-02-28T14:10:51.966269', 'YYYY-MM-DD') // '2026-02-28'
|
||||
* formatISOToDateTime('2026-02-28T14:10:51.966269', 'YYYY/MM/DD HH:mm') // '2026/02/28 14:10'
|
||||
* formatISOToDateTime('2026-02-28T14:10:51.966269', 'MM-DD HH:mm') // '02-28 14:10'
|
||||
* formatISOToDateTime('2026-02-28T14:10:51.966269', 'YYYY 年 MM 月 DD 日') // '2026 年 02 月 28 日'
|
||||
*/
|
||||
export function formatISOToDateTime(isoString: string): string {
|
||||
export function formatISOToDateTime(isoString: string, format = 'YYYY-MM-DD HH:mm:ss'): string {
|
||||
if (!isoString) return ''
|
||||
|
||||
try {
|
||||
@@ -210,7 +218,14 @@ export function formatISOToDateTime(isoString: string): string {
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0')
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0')
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||||
// 替换格式化模板中的占位符
|
||||
return format
|
||||
.replace('YYYY', String(year))
|
||||
.replace('MM', month)
|
||||
.replace('DD', day)
|
||||
.replace('HH', hours)
|
||||
.replace('mm', minutes)
|
||||
.replace('ss', seconds)
|
||||
} catch (error) {
|
||||
console.error('日期格式化错误:', error)
|
||||
return isoString
|
||||
|
||||
199
src/views/DataStatistics/components/DailyDataBreakdown.vue
Normal file
199
src/views/DataStatistics/components/DailyDataBreakdown.vue
Normal file
@@ -0,0 +1,199 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div class="chart-header">
|
||||
<div class="chart-title">每日数据明细</div>
|
||||
<el-radio-group v-model="queryParams.days" size="small" @change="handleTimeChange">
|
||||
<el-radio-button v-for="item in timeOptions" :key="item.value" :value="item.value">
|
||||
{{ item.label }}
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<el-card shadow="hover" class="data-table">
|
||||
<div class="data-table__toolbar">
|
||||
<div class="data-table__toolbar--actions">
|
||||
<!-- <el-button type="success" icon="plus" @click="handleOpenDialog()">新增</el-button>-->
|
||||
</div>
|
||||
</div>
|
||||
<el-table
|
||||
ref="dataTableRef"
|
||||
v-loading="loading"
|
||||
:data="roleList"
|
||||
highlight-current-row
|
||||
border
|
||||
class="data-table__content"
|
||||
>
|
||||
<el-table-column label="联系人数" prop="contacts" />
|
||||
<el-table-column label="回复人数" prop="replied" />
|
||||
<el-table-column label="微信相关数量" prop="wechat"></el-table-column>
|
||||
<el-table-column label="回复率" prop="reply_rate" />
|
||||
<el-table-column label="日期时间" prop="date">
|
||||
<template #default="scope">
|
||||
{{ formatISOToDateTime(scope.row?.date || '') }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-if="total > 0"
|
||||
v-model:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="fetchData"
|
||||
/>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ApiStatsDaily } from '@/api/ContactInformation'
|
||||
import { formatISOToDateTime } from '@/utils/auxiliaryFunction'
|
||||
import { ref } from 'vue'
|
||||
|
||||
defineOptions({
|
||||
name: 'Role',
|
||||
inheritAttrs: false
|
||||
})
|
||||
|
||||
// const queryFormRef = ref()
|
||||
|
||||
const loading = ref(false)
|
||||
const total = ref(0)
|
||||
|
||||
const queryParams = reactive<any>({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
days: '1'
|
||||
})
|
||||
|
||||
// 表格数据
|
||||
const roleList = ref<any[]>()
|
||||
|
||||
const timeOptions = [
|
||||
{ label: '今日', value: '1' },
|
||||
{ label: '近 7 天', value: '7' },
|
||||
{ label: '近 30 天', value: '30' }
|
||||
]
|
||||
|
||||
// // 弹窗
|
||||
// const dialog = reactive({
|
||||
// title: '',
|
||||
// visible: false
|
||||
// })
|
||||
|
||||
// 获取数据
|
||||
function fetchData() {
|
||||
loading.value = true
|
||||
ApiStatsDaily(queryParams)
|
||||
.then((res: any) => {
|
||||
roleList.value = res.data
|
||||
// total.value = res.data.total
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
// 查询(重置页码后获取数据)
|
||||
function handleQuery() {
|
||||
queryParams.pageNum = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
// // 重置查询
|
||||
// function handleResetQuery() {
|
||||
// if (queryFormRef.value) queryFormRef.value?.resetFields()
|
||||
// queryParams.pageNum = 1
|
||||
// fetchData()
|
||||
// }
|
||||
|
||||
const handleTimeChange = () => {
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
handleQuery()
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.headline-statistics {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-bottom: 16px;
|
||||
height: 140px;
|
||||
}
|
||||
::v-deep(.statistics-box) {
|
||||
display: flex;
|
||||
//justify-content: center;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
font-size: 30px;
|
||||
gap: 20px;
|
||||
.statistics-box-img {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 15px;
|
||||
}
|
||||
.img-bg1 {
|
||||
background: linear-gradient(135deg, #7f7fd5 0%, #9156e8 100%);
|
||||
}
|
||||
.img-bg2 {
|
||||
background: linear-gradient(135deg, #f199ee 0%, #ef6b8b 100%);
|
||||
}
|
||||
.img-bg3 {
|
||||
background: linear-gradient(135deg, #56bafd 0%, #16e9fd 100%);
|
||||
}
|
||||
.img-bg4 {
|
||||
background: linear-gradient(135deg, #51eb90 0%, #49f7d2 100%);
|
||||
}
|
||||
.statistics-box-text {
|
||||
//flex: 1;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
color: #adb0b3;
|
||||
& > span:nth-child(1) {
|
||||
font-size: 14px;
|
||||
}
|
||||
& > span:nth-child(2) {
|
||||
font-size: 26px;
|
||||
color: #000;
|
||||
font-weight: 700;
|
||||
}
|
||||
& > span:nth-child(3) {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.chart-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.chart-title {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.time-filter {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
||||
:deep(.el-button) {
|
||||
padding: 5px 12px;
|
||||
font-size: 13px;
|
||||
|
||||
&.el-button--primary {
|
||||
background-color: #409eff;
|
||||
border-color: #409eff;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
224
src/views/DataStatistics/components/DataTrendChart.vue
Normal file
224
src/views/DataStatistics/components/DataTrendChart.vue
Normal file
@@ -0,0 +1,224 @@
|
||||
<template>
|
||||
<div class="chart-container">
|
||||
<div ref="chartRef" class="trend-chart"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, onUnmounted, ref, watch } from 'vue'
|
||||
import * as echarts from 'echarts'
|
||||
import type { EChartsOption } from 'echarts'
|
||||
import { ApiStatsDaily } from '@/api/ContactInformation'
|
||||
import { formatISOToDateTime } from '@/utils/auxiliaryFunction'
|
||||
|
||||
interface Props {
|
||||
height?: string
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
height: '350px'
|
||||
})
|
||||
|
||||
const data = ref<any>({
|
||||
xAxis: [] as string[],
|
||||
series: [
|
||||
{
|
||||
name: '联系人数',
|
||||
data: []
|
||||
},
|
||||
{
|
||||
name: '已回复',
|
||||
data: []
|
||||
},
|
||||
{
|
||||
name: '微信交换',
|
||||
data: []
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
const chartRef = ref<HTMLElement | null>(null)
|
||||
let chartInstance: echarts.ECharts | null = null
|
||||
|
||||
// 颜色配置
|
||||
const seriesColors = ['#5B9BFF', '#67C23A', '#E6A23C', '#F56C6C', '#909399']
|
||||
|
||||
// 初始化图表
|
||||
const initChart = () => {
|
||||
if (!chartRef.value) return
|
||||
|
||||
chartInstance = echarts.init(chartRef.value)
|
||||
|
||||
const option: EChartsOption = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '15%',
|
||||
top: '10%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: data.value.xAxis,
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#D7DDE3'
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#909399',
|
||||
margin: 20,
|
||||
rotate: 45,
|
||||
interval: 0,
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
min: 0,
|
||||
max: 1,
|
||||
interval: 0.2,
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#E8E8E8',
|
||||
type: 'solid'
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#909399',
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
series: data.value.series.map((item: any, index: number) => ({
|
||||
name: item.name,
|
||||
type: 'bar',
|
||||
barGap: '10%',
|
||||
data: item.data,
|
||||
itemStyle: {
|
||||
color: seriesColors[index % seriesColors.length],
|
||||
borderRadius: [2, 2, 0, 0]
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
opacity: 0.8
|
||||
}
|
||||
}
|
||||
})),
|
||||
legend: {
|
||||
data: data.value.series.map((item: any) => item.name),
|
||||
bottom: 0,
|
||||
icon: 'rect',
|
||||
itemWidth: 14,
|
||||
itemHeight: 14,
|
||||
itemGap: 20,
|
||||
textStyle: {
|
||||
color: '#606266',
|
||||
fontSize: 12
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chartInstance.setOption(option)
|
||||
}
|
||||
|
||||
const onApiStatsDaily = (days: string) => {
|
||||
ApiStatsDaily({ days }).then((res: { data: any[] }) => {
|
||||
data.value = {
|
||||
xAxis: res.data?.map((item) => formatISOToDateTime(item.date, 'YYYY-MM-DD')),
|
||||
series: [
|
||||
{
|
||||
name: '联系人数',
|
||||
data: res.data?.map((item) => item.contacts)
|
||||
},
|
||||
{
|
||||
name: '已回复',
|
||||
data: res.data?.map((item) => item.replied)
|
||||
},
|
||||
{
|
||||
name: '微信交换',
|
||||
data: res.data?.map((item) => item.wechat)
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 监听数据变化
|
||||
watch(
|
||||
() => data.value,
|
||||
(newData: any) => {
|
||||
if (chartInstance) {
|
||||
chartInstance.setOption({
|
||||
xAxis: { data: newData.xAxis },
|
||||
series: newData.series.map((item: any, index: number) => ({
|
||||
name: item.name,
|
||||
data: item.data,
|
||||
itemStyle: {
|
||||
color: seriesColors[index % seriesColors.length]
|
||||
}
|
||||
})),
|
||||
legend: {
|
||||
data: newData.series.map((item: any) => item.name)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
// 监听窗口大小变化
|
||||
const handleResize = () => {
|
||||
if (chartInstance) {
|
||||
chartInstance.resize()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initChart()
|
||||
window.addEventListener('resize', handleResize)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose()
|
||||
chartInstance = null
|
||||
}
|
||||
window.removeEventListener('resize', handleResize)
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
resize: handleResize,
|
||||
onApiStatsDaily
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.chart-container {
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.trend-chart {
|
||||
width: 100%;
|
||||
height: v-bind(height);
|
||||
}
|
||||
</style>
|
||||
7
src/views/DataStatistics/components/TaskDetails.vue
Normal file
7
src/views/DataStatistics/components/TaskDetails.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<div>TaskDetails</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
110
src/views/DataStatistics/components/TaskForm.vue
Normal file
110
src/views/DataStatistics/components/TaskForm.vue
Normal file
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<div class="pre-registration-form">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="auto"
|
||||
label-position="top"
|
||||
>
|
||||
<el-form-item label="任务类型" prop="task_type">
|
||||
<!-- <el-input v-model="formData.task_type" placeholder="请输入" />-->
|
||||
<el-select v-model="formData.task_type" placeholder="请选择">
|
||||
<el-option label="检查登录" value="check_login" />
|
||||
<el-option label="招聘" value="boss_recruit" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="任务参数" prop="params">
|
||||
<el-input v-model="formData.params" placeholder="请输入" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { deepCloneByJSON } from '@/utils/auxiliaryFunction'
|
||||
|
||||
const props = defineProps({
|
||||
newData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
})
|
||||
|
||||
const formRef = ref()
|
||||
// 表单数据
|
||||
const formData = reactive<any>({
|
||||
task_type: '',
|
||||
worker_id: '',
|
||||
account_name: '',
|
||||
params: ''
|
||||
})
|
||||
const formRules = reactive<any>({
|
||||
task_type: [{ required: true, message: '请输入', trigger: 'blur' }],
|
||||
params: [{ required: true, message: '请输入', trigger: 'blur' }]
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
setFormData(props.newData)
|
||||
})
|
||||
|
||||
const getForm = () => {
|
||||
return formData
|
||||
}
|
||||
|
||||
const setFormData = (data: any) => {
|
||||
if (data && Object.keys(data).length > 0) {
|
||||
const data1 = deepCloneByJSON(data)
|
||||
|
||||
Object.assign(formData, data1)
|
||||
}
|
||||
}
|
||||
|
||||
const submit = (): Promise<boolean> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
formRef.value?.validate((valid: boolean) => {
|
||||
if (valid) {
|
||||
resolve(true)
|
||||
} else {
|
||||
ElMessage.error('请完善必填信息')
|
||||
reject(false)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
defineExpose({
|
||||
submit,
|
||||
getForm
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.pre-registration-form {
|
||||
width: 100%;
|
||||
padding-right: 20px;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
}
|
||||
.section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
</style>
|
||||
197
src/views/DataStatistics/index.vue
Normal file
197
src/views/DataStatistics/index.vue
Normal file
@@ -0,0 +1,197 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div class="headline-statistics">
|
||||
<el-card shadow="hover" body-class="statistics-box" style="flex: 1">
|
||||
<div class="statistics-box-img img-bg1">
|
||||
<el-icon><User /></el-icon>
|
||||
</div>
|
||||
<div class="statistics-box-text">
|
||||
<span>总联系人数</span>
|
||||
<span>{{ apiStatsData.contacts?.total || 0 }}</span>
|
||||
<span>今日:{{ apiStatsData.contacts?.today || 0 }}</span>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card shadow="hover" body-class="statistics-box" style="flex: 1">
|
||||
<div class="statistics-box-img img-bg2">
|
||||
<el-icon><ChatDotRound /></el-icon>
|
||||
</div>
|
||||
<div class="statistics-box-text">
|
||||
<span>已回复人数</span>
|
||||
<span>{{ apiStatsData.contacts?.replied || 0 }}</span>
|
||||
<span>回复率:{{ apiStatsData.contacts?.reply_rate || 0 }}%</span>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card shadow="hover" body-class="statistics-box" style="flex: 1">
|
||||
<div class="statistics-box-img img-bg3">
|
||||
<el-icon><ChatLineRound /></el-icon>
|
||||
</div>
|
||||
<div class="statistics-box-text">
|
||||
<span>微信交换数</span>
|
||||
<span>{{ apiStatsData.wechat?.total || 0 }}</span>
|
||||
<span>成功率:{{ apiStatsData.wechat?.success_rate || 0 }}%</span>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card shadow="hover" body-class="statistics-box" style="flex: 1">
|
||||
<div class="statistics-box-img img-bg4">
|
||||
<el-icon><Monitor /></el-icon>
|
||||
</div>
|
||||
<div class="statistics-box-text">
|
||||
<span>在线Worker</span>
|
||||
<span>{{ apiStatsData?.accounts?.logged_in || 0 }}</span>
|
||||
<span>总数:{{ apiStatsData?.accounts?.total || 0 }}</span>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
<el-card shadow="hover">
|
||||
<div class="chart-header">
|
||||
<div class="chart-title">数据趋势</div>
|
||||
<el-radio-group v-model="currentTime" size="small" @change="handleTimeChange">
|
||||
<el-radio-button v-for="item in timeOptions" :key="item.value" :value="item.value">
|
||||
{{ item.label }}
|
||||
</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<DataTrendChart ref="dataTrendChartRef" />
|
||||
</el-card>
|
||||
<el-card shadow="hover">
|
||||
<DailyDataBreakdown></DailyDataBreakdown>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ApiStats } from '@/api/ContactInformation'
|
||||
import { Monitor, User, ChatDotRound, ChatLineRound } from '@element-plus/icons-vue'
|
||||
import DataTrendChart from './components/DataTrendChart.vue'
|
||||
import { ref } from 'vue'
|
||||
import DailyDataBreakdown from '@/views/DataStatistics/components/DailyDataBreakdown.vue'
|
||||
|
||||
defineOptions({
|
||||
name: 'Role',
|
||||
inheritAttrs: false
|
||||
})
|
||||
|
||||
// const queryFormRef = ref()
|
||||
const dataTrendChartRef = ref()
|
||||
|
||||
// 数据
|
||||
const currentTime = ref('today')
|
||||
const timeOptions = [
|
||||
{ label: '今日', value: 'today' },
|
||||
{ label: '近 7 天', value: 'week' },
|
||||
{ label: '近 30 天', value: 'month' },
|
||||
{ label: '全部', value: 'all' }
|
||||
]
|
||||
const apiStatsData = ref<any>({})
|
||||
// // 弹窗
|
||||
// const dialog = reactive({
|
||||
// title: '',
|
||||
// visible: false
|
||||
// })
|
||||
|
||||
// 获取数据
|
||||
const onApiStats = () => {
|
||||
ApiStats({ period: currentTime.value }).then((res: any) => {
|
||||
apiStatsData.value = res.data
|
||||
})
|
||||
}
|
||||
|
||||
// // 重置查询
|
||||
// function handleResetQuery() {
|
||||
// if (queryFormRef.value) queryFormRef.value?.resetFields()
|
||||
// queryParams.pageNum = 1
|
||||
// fetchData()
|
||||
// }
|
||||
|
||||
const handleTimeChange = () => {
|
||||
onApiStats()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
onApiStats()
|
||||
dataTrendChartRef.value?.onApiStatsDaily('7')
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.headline-statistics {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-bottom: 16px;
|
||||
height: 140px;
|
||||
}
|
||||
::v-deep(.statistics-box) {
|
||||
display: flex;
|
||||
//justify-content: center;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
font-size: 30px;
|
||||
gap: 20px;
|
||||
.statistics-box-img {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 15px;
|
||||
}
|
||||
.img-bg1 {
|
||||
background: linear-gradient(135deg, #7f7fd5 0%, #9156e8 100%);
|
||||
}
|
||||
.img-bg2 {
|
||||
background: linear-gradient(135deg, #f199ee 0%, #ef6b8b 100%);
|
||||
}
|
||||
.img-bg3 {
|
||||
background: linear-gradient(135deg, #56bafd 0%, #16e9fd 100%);
|
||||
}
|
||||
.img-bg4 {
|
||||
background: linear-gradient(135deg, #51eb90 0%, #49f7d2 100%);
|
||||
}
|
||||
.statistics-box-text {
|
||||
//flex: 1;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
color: #adb0b3;
|
||||
& > span:nth-child(1) {
|
||||
font-size: 14px;
|
||||
}
|
||||
& > span:nth-child(2) {
|
||||
font-size: 26px;
|
||||
color: #000;
|
||||
font-weight: 700;
|
||||
}
|
||||
& > span:nth-child(3) {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.chart-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.chart-title {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.time-filter {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
||||
:deep(.el-button) {
|
||||
padding: 5px 12px;
|
||||
font-size: 13px;
|
||||
|
||||
&.el-button--primary {
|
||||
background-color: #409eff;
|
||||
border-color: #409eff;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user