refactor(话术管理): 重构话术列表和编辑界面,调整API路径 fix(请求拦截器): 移除调试日志并优化错误处理 style(分页组件): 格式化代码并添加初始化标记 feat(知识库): 实现文档状态切换和向量生成功能
1346 lines
37 KiB
Vue
1346 lines
37 KiB
Vue
<template>
|
|
<div class="ads-summary-monitor">
|
|
<el-card shadow="hover">
|
|
<template #header>
|
|
<div class="card-header">
|
|
<span>监控总表</span>
|
|
<el-button type="text" @click="openSettingDialog" class="setting-btn">
|
|
<el-icon><Setting /></el-icon>
|
|
<span>设置</span>
|
|
</el-button>
|
|
</div>
|
|
</template>
|
|
<el-tabs v-model="activeTab" class="tabs-container">
|
|
<el-tab-pane label="数据趋势" name="trend">
|
|
<div class="chart-container">
|
|
<el-row :gutter="20">
|
|
<el-col :span="24">
|
|
<el-card>
|
|
<template #header>
|
|
<div class="card-header">预算执行趋势</div>
|
|
</template>
|
|
<div ref="trendChartRef" class="chart" style="width: 100%; height: 600px"></div>
|
|
</el-card>
|
|
</el-col>
|
|
</el-row>
|
|
</div>
|
|
<div class="table-container">
|
|
<el-card>
|
|
<template #header>
|
|
<div class="card-header">
|
|
<span>部门执行明细</span>
|
|
<el-form :model="searchParams" :inline="true" class="search-form">
|
|
<el-form-item label="时间范围">
|
|
<el-date-picker
|
|
v-model="searchParams.dateRange"
|
|
type="daterange"
|
|
range-separator="至"
|
|
start-placeholder="开始日期"
|
|
end-placeholder="结束日期"
|
|
value-format="YYYY-MM-DD"
|
|
:shortcuts="dateShortcuts"
|
|
/>
|
|
</el-form-item>
|
|
<el-form-item label="部门">
|
|
<el-select v-model="searchParams.department" placeholder="选择部门">
|
|
<el-option v-for="option in departmentOptions" :key="option.value || 'all'" :label="option.label" :value="option.value" />
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item>
|
|
<el-button type="primary" @click="handleSearch">查询</el-button>
|
|
<el-button @click="handleReset">重置</el-button>
|
|
</el-form-item>
|
|
</el-form>
|
|
</div>
|
|
</template>
|
|
<el-table :data="trendTableData" style="width: 100%">
|
|
<el-table-column prop="department" label="部门" />
|
|
<el-table-column prop="data" label="数据" />
|
|
</el-table>
|
|
</el-card>
|
|
</div>
|
|
</el-tab-pane>
|
|
<el-tab-pane label="部门分析" name="department">
|
|
<div class="chart-container">
|
|
<el-row :gutter="20">
|
|
<el-col :span="24">
|
|
<el-card>
|
|
<template #header>
|
|
<div class="card-header">部门达成率分析</div>
|
|
</template>
|
|
<div ref="departmentChartRef" class="chart" style="width: 100%; height: 600px"></div>
|
|
</el-card>
|
|
</el-col>
|
|
</el-row>
|
|
</div>
|
|
<div class="table-container">
|
|
<el-card>
|
|
<template #header>
|
|
<div class="card-header">
|
|
<span>部门预算执行明细</span>
|
|
<el-form :model="searchParams" :inline="true" class="search-form">
|
|
<el-form-item label="时间范围">
|
|
<el-date-picker
|
|
v-model="searchParams.dateRange"
|
|
type="daterange"
|
|
range-separator="至"
|
|
start-placeholder="开始日期"
|
|
end-placeholder="结束日期"
|
|
value-format="YYYY-MM-DD"
|
|
:shortcuts="dateShortcuts"
|
|
/>
|
|
</el-form-item>
|
|
<el-form-item label="部门">
|
|
<el-select v-model="searchParams.department" placeholder="选择部门">
|
|
<el-option v-for="option in departmentOptions" :key="option.value || 'all'" :label="option.label" :value="option.value" />
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item>
|
|
<el-button type="primary" @click="handleSearch">查询</el-button>
|
|
<el-button @click="handleReset">重置</el-button>
|
|
</el-form-item>
|
|
</el-form>
|
|
</div>
|
|
</template>
|
|
<el-table :data="departmentTableData" style="width: 100%">
|
|
<el-table-column prop="department" label="部门" />
|
|
<el-table-column prop="monthlyBudget" label="月度任务" />
|
|
<el-table-column prop="executed" label="已达成" />
|
|
<el-table-column prop="achievementRate" label="达成率">
|
|
<template #default="scope">{{ scope.row.achievementRate }}%</template>
|
|
</el-table-column>
|
|
<el-table-column prop="surplusBudget" label="剩余任务" />
|
|
<el-table-column prop="dailyAverage" label="剩余日均" />
|
|
<el-table-column prop="yesterday" label="昨日" />
|
|
<el-table-column prop="estimatedAchievement" label="预计达成" />
|
|
<el-table-column prop="dailyDifference" label="日均差" />
|
|
<el-table-column prop="previousDay" label="前日" />
|
|
<el-table-column prop="sequentialRatio" label="环比" />
|
|
<el-table-column prop="timeProgress" label="对比时间进度" />
|
|
<el-table-column prop="januaryAchievement" label="1月达成" />
|
|
<el-table-column prop="januarySequentialRatio" label="环比" />
|
|
<el-table-column prop="differenceFromTarget" label="目标差" />
|
|
</el-table>
|
|
</el-card>
|
|
</div>
|
|
</el-tab-pane>
|
|
<el-tab-pane label="任务进度" name="progress">
|
|
<div class="chart-container">
|
|
<el-row :gutter="20">
|
|
<el-col :span="24">
|
|
<el-card>
|
|
<template #header>
|
|
<div class="card-header">任务进度分析</div>
|
|
</template>
|
|
<div ref="progressChartRef" class="chart" style="width: 100%; height: 600px"></div>
|
|
</el-card>
|
|
</el-col>
|
|
</el-row>
|
|
</div>
|
|
<div class="table-container">
|
|
<el-card>
|
|
<template #header>
|
|
<div class="card-header">
|
|
<span>任务进度明细</span>
|
|
<el-form :model="searchParams" :inline="true" class="search-form">
|
|
<el-form-item label="时间范围">
|
|
<el-date-picker
|
|
v-model="searchParams.dateRange"
|
|
type="daterange"
|
|
range-separator="至"
|
|
start-placeholder="开始日期"
|
|
end-placeholder="结束日期"
|
|
value-format="YYYY-MM-DD"
|
|
:shortcuts="dateShortcuts"
|
|
/>
|
|
</el-form-item>
|
|
<el-form-item label="部门">
|
|
<el-select v-model="searchParams.department" placeholder="选择部门">
|
|
<el-option v-for="option in departmentOptions" :key="option.value || 'all'" :label="option.label" :value="option.value" />
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item>
|
|
<el-button type="primary" @click="handleSearch">查询</el-button>
|
|
<el-button @click="handleReset">重置</el-button>
|
|
</el-form-item>
|
|
</el-form>
|
|
</div>
|
|
</template>
|
|
<el-table :data="progressTableData" style="width: 100%">
|
|
<el-table-column prop="date" label="日期" />
|
|
<el-table-column prop="quarterFirstDay" label="季度第一天" />
|
|
<el-table-column prop="quarterLastDay" label="季度最后一天" />
|
|
<el-table-column prop="textAudit" label="文本审核" />
|
|
<el-table-column prop="homeCount" label="家居次数" />
|
|
<el-table-column prop="completedCount" label="已完成次数" />
|
|
<el-table-column prop="remainingDays" label="剩余天数" />
|
|
<el-table-column prop="quarterSecondMonth" label="季度二月" />
|
|
<el-table-column prop="textAudit2" label="文本审核2" />
|
|
</el-table>
|
|
</el-card>
|
|
</div>
|
|
</el-tab-pane>
|
|
<el-tab-pane label="客户监控" name="customer">
|
|
<div class="chart-container">
|
|
<el-row :gutter="20">
|
|
<el-col :span="24">
|
|
<el-card>
|
|
<template #header>
|
|
<div class="card-header">客户数据趋势</div>
|
|
</template>
|
|
<div ref="customerChartRef" class="chart" style="width: 100%; height: 600px"></div>
|
|
</el-card>
|
|
</el-col>
|
|
</el-row>
|
|
</div>
|
|
<div class="table-container">
|
|
<el-card>
|
|
<template #header>
|
|
<div class="card-header">
|
|
<span>监控总表</span>
|
|
<el-form :model="customerSearchParams" :inline="true" class="search-form">
|
|
<el-form-item label="客户">
|
|
<el-input v-model="customerSearchParams.customer" placeholder="搜索客户" style="width: 180px" />
|
|
</el-form-item>
|
|
<el-form-item label="状态">
|
|
<el-select v-model="customerSearchParams.status" placeholder="选择状态" style="width: 120px">
|
|
<el-option label="全部" value="" />
|
|
<el-option label="正常" value="normal" />
|
|
<el-option label="异常" value="abnormal" />
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item>
|
|
<el-button type="primary" @click="handleCustomerSearch">查询</el-button>
|
|
<el-button @click="handleCustomerReset">重置</el-button>
|
|
</el-form-item>
|
|
</el-form>
|
|
</div>
|
|
</template>
|
|
<el-table :data="customerTableData" style="width: 100%" border>
|
|
<el-table-column prop="customer" label="客户" width="150" />
|
|
<el-table-column prop="date1" label="2026/2/7" width="120">
|
|
<template #default="scope">
|
|
<span :class="scope.row.date1 >= 0 ? 'text-green' : 'text-red'">
|
|
{{ scope.row.date1 }}
|
|
</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="date2" label="2026/2/8" width="120">
|
|
<template #default="scope">
|
|
<span :class="scope.row.date2 >= 0 ? 'text-green' : 'text-red'">
|
|
{{ scope.row.date2 }}
|
|
</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="date3" label="2026/2/9" width="120">
|
|
<template #default="scope">
|
|
<span :class="scope.row.date3 >= 0 ? 'text-green' : 'text-red'">
|
|
{{ scope.row.date3 }}
|
|
</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="date4" label="2026/2/10" width="120">
|
|
<template #default="scope">
|
|
<span :class="scope.row.date4 >= 0 ? 'text-green' : 'text-red'">
|
|
{{ scope.row.date4 }}
|
|
</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="date5" label="2026/2/11" width="120">
|
|
<template #default="scope">
|
|
<span :class="scope.row.date5 >= 0 ? 'text-green' : 'text-red'">
|
|
{{ scope.row.date5 }}
|
|
</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="date6" label="2026/2/12" width="120">
|
|
<template #default="scope">
|
|
<span :class="scope.row.date6 >= 0 ? 'text-green' : 'text-red'">
|
|
{{ scope.row.date6 }}
|
|
</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="date7" label="2026/2/15" width="120">
|
|
<template #default="scope">
|
|
<span :class="scope.row.date7 >= 0 ? 'text-green' : 'text-red'">
|
|
{{ scope.row.date7 }}
|
|
</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="date8" label="2026/2/16" width="120">
|
|
<template #default="scope">
|
|
<span :class="scope.row.date8 >= 0 ? 'text-green' : 'text-red'">
|
|
{{ scope.row.date8 }}
|
|
</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="date9" label="2026/2/17" width="120">
|
|
<template #default="scope">
|
|
<span :class="scope.row.date9 >= 0 ? 'text-green' : 'text-red'">
|
|
{{ scope.row.date9 }}
|
|
</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="date10" label="2026/2/18" width="120">
|
|
<template #default="scope">
|
|
<span :class="scope.row.date10 >= 0 ? 'text-green' : 'text-red'">
|
|
{{ scope.row.date10 }}
|
|
</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="date11" label="2026/2/19" width="120">
|
|
<template #default="scope">
|
|
<span :class="scope.row.date11 >= 0 ? 'text-green' : 'text-red'">
|
|
{{ scope.row.date11 }}
|
|
</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="date12" label="2026/2/20" width="120">
|
|
<template #default="scope">
|
|
<span :class="scope.row.date12 >= 0 ? 'text-green' : 'text-red'">
|
|
{{ scope.row.date12 }}
|
|
</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="date13" label="2026/2/21" width="120">
|
|
<template #default="scope">
|
|
<span :class="scope.row.date13 >= 0 ? 'text-green' : 'text-red'">
|
|
{{ scope.row.date13 }}
|
|
</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="date14" label="2026/2/22" width="120">
|
|
<template #default="scope">
|
|
<span :class="scope.row.date14 >= 0 ? 'text-green' : 'text-red'">
|
|
{{ scope.row.date14 }}
|
|
</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="date15" label="2026/2/23" width="120">
|
|
<template #default="scope">
|
|
<span :class="scope.row.date15 >= 0 ? 'text-green' : 'text-red'">
|
|
{{ scope.row.date15 }}
|
|
</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="date16" label="2026/2/24" width="120">
|
|
<template #default="scope">
|
|
<span :class="scope.row.date16 >= 0 ? 'text-green' : 'text-red'">
|
|
{{ scope.row.date16 }}
|
|
</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="date17" label="2026/2/25" width="120">
|
|
<template #default="scope">
|
|
<span :class="scope.row.date17 >= 0 ? 'text-green' : 'text-red'">
|
|
{{ scope.row.date17 }}
|
|
</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="date18" label="2026/2/26" width="120">
|
|
<template #default="scope">
|
|
<span :class="scope.row.date18 >= 0 ? 'text-green' : 'text-red'">
|
|
{{ scope.row.date18 }}
|
|
</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="date19" label="2026/2/27" width="120">
|
|
<template #default="scope">
|
|
<span :class="scope.row.date19 >= 0 ? 'text-green' : 'text-red'">
|
|
{{ scope.row.date19 }}
|
|
</span>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column prop="date20" label="2026/2/28" width="120">
|
|
<template #default="scope">
|
|
<span :class="scope.row.date20 >= 0 ? 'text-green' : 'text-red'">
|
|
{{ scope.row.date20 }}
|
|
</span>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
</el-card>
|
|
</div>
|
|
</el-tab-pane>
|
|
</el-tabs>
|
|
|
|
<el-dialog v-model="dialogVisible" title="任务设置" width="400px">
|
|
<el-form :model="taskSettings" label-width="120px">
|
|
<el-form-item label="任务类型">
|
|
<el-radio-group v-model="taskSettings.taskType">
|
|
<el-radio label="monthly">月度任务</el-radio>
|
|
<el-radio label="quarterly">季度任务</el-radio>
|
|
</el-radio-group>
|
|
</el-form-item>
|
|
<el-form-item label="总任务金额">
|
|
<el-input-number v-model="taskSettings.totalBudget" :min="0" :step="10000" />
|
|
</el-form-item>
|
|
<el-form-item label="部门任务分配">
|
|
<el-button type="primary" @click="openDepartmentTaskDialog">设置部门任务</el-button>
|
|
</el-form-item>
|
|
</el-form>
|
|
<template #footer>
|
|
<span class="dialog-footer">
|
|
<el-button @click="dialogVisible = false">取消</el-button>
|
|
<el-button type="primary" @click="handleSaveSettings">保存设置</el-button>
|
|
</span>
|
|
</template>
|
|
</el-dialog>
|
|
|
|
<el-dialog v-model="departmentTaskDialogVisible" title="部门任务分配" width="600px">
|
|
<el-table :data="departmentTasks" style="width: 100%">
|
|
<el-table-column prop="department" label="部门" />
|
|
<el-table-column prop="budget" label="任务金额">
|
|
<template #default="scope">
|
|
<el-input-number v-model="scope.row.budget" :min="0" :step="10000" />
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
<template #footer>
|
|
<span class="dialog-footer">
|
|
<el-button @click="departmentTaskDialogVisible = false">取消</el-button>
|
|
<el-button type="primary" @click="saveDepartmentTasks">保存分配</el-button>
|
|
</span>
|
|
</template>
|
|
</el-dialog>
|
|
</el-card>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { onMounted, reactive, ref, watch, nextTick } from 'vue';
|
|
import * as echarts from 'echarts';
|
|
import { Setting } from '@element-plus/icons-vue';
|
|
|
|
interface DepartmentData {
|
|
department: string;
|
|
monthlyBudget: string;
|
|
executed: string;
|
|
achievementRate: string;
|
|
surplusBudget: string;
|
|
surplusDays: string;
|
|
dailyAverage: string;
|
|
yesterday: string;
|
|
estimatedAchievement: string;
|
|
dailyDifference: string;
|
|
previousDay: string;
|
|
sequentialRatio: string;
|
|
timeProgress: string;
|
|
januaryAchievement: string;
|
|
januarySequentialRatio: string;
|
|
differenceFromTarget: string;
|
|
}
|
|
|
|
interface TrendData {
|
|
department: string;
|
|
data: string;
|
|
}
|
|
|
|
interface ProgressData {
|
|
date: string;
|
|
quarterFirstDay: string;
|
|
quarterLastDay: string;
|
|
textAudit: string;
|
|
homeCount: string;
|
|
completedCount: string;
|
|
remainingDays: string;
|
|
quarterSecondMonth: string;
|
|
textAudit2: string;
|
|
}
|
|
|
|
interface DepartmentTask {
|
|
department: string;
|
|
budget: number;
|
|
}
|
|
|
|
interface CustomerData {
|
|
customer: string;
|
|
date1: number;
|
|
date2: number;
|
|
date3: number;
|
|
date4: number;
|
|
date5: number;
|
|
date6: number;
|
|
date7: number;
|
|
date8: number;
|
|
date9: number;
|
|
date10: number;
|
|
date11: number;
|
|
date12: number;
|
|
date13: number;
|
|
date14: number;
|
|
date15: number;
|
|
date16: number;
|
|
date17: number;
|
|
date18: number;
|
|
date19: number;
|
|
date20: number;
|
|
}
|
|
|
|
const departmentOptions = [
|
|
{ label: '全部', value: 'all' },
|
|
{ label: '一区一部', value: '一区一部' },
|
|
{ label: '一区二部', value: '一区二部' },
|
|
{ label: '一区三部', value: '一区三部' },
|
|
{ label: '一区四部', value: '一区四部' },
|
|
{ label: '代理运营总', value: '代理运营总' },
|
|
{ label: '渠道部', value: '渠道部' },
|
|
{ label: '电商一部', value: '电商一部' },
|
|
{ label: '电商二部', value: '电商二部' },
|
|
{ label: '张哥自营', value: '张哥自营' },
|
|
{ label: '电商合计', value: '电商合计' },
|
|
{ label: '金牛总任务', value: '金牛总任务' },
|
|
];
|
|
|
|
const searchParams = reactive({
|
|
dateRange: [],
|
|
department: '',
|
|
});
|
|
|
|
const customerSearchParams = reactive({
|
|
customer: '',
|
|
status: '',
|
|
});
|
|
|
|
const dateShortcuts = [
|
|
{
|
|
text: '最近7天',
|
|
value: () => {
|
|
const end = new Date();
|
|
const start = new Date();
|
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
|
|
return [start, end];
|
|
},
|
|
},
|
|
{
|
|
text: '最近30天',
|
|
value: () => {
|
|
const end = new Date();
|
|
const start = new Date();
|
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
|
|
return [start, end];
|
|
},
|
|
},
|
|
{
|
|
text: '最近90天',
|
|
value: () => {
|
|
const end = new Date();
|
|
const start = new Date();
|
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
|
|
return [start, end];
|
|
},
|
|
},
|
|
];
|
|
|
|
const activeTab = ref('trend');
|
|
const dialogVisible = ref(false);
|
|
const departmentTaskDialogVisible = ref(false);
|
|
|
|
const taskSettings = reactive({
|
|
taskType: 'monthly',
|
|
totalBudget: 52000000,
|
|
});
|
|
|
|
const departmentTasks = ref<DepartmentTask[]>([
|
|
{ department: '一区一部', budget: 1300000 },
|
|
{ department: '一区二部', budget: 3500000 },
|
|
{ department: '一区三部', budget: 2100000 },
|
|
{ department: '一区四部', budget: 1600000 },
|
|
{ department: '代理运营总', budget: 8500000 },
|
|
{ department: '渠道部', budget: 3000000 },
|
|
{ department: '电商一部', budget: 4700000 },
|
|
{ department: '电商二部', budget: 2800000 },
|
|
{ department: '张哥自营', budget: 6000000 },
|
|
{ department: '电商合计', budget: 13500000 },
|
|
{ department: '金牛总任务', budget: 52000000 },
|
|
]);
|
|
|
|
const trendChartRef = ref();
|
|
const departmentChartRef = ref();
|
|
const progressChartRef = ref();
|
|
const customerChartRef = ref();
|
|
let trendChart: echarts.ECharts | null = null;
|
|
let departmentChart: echarts.ECharts | null = null;
|
|
let progressChart: echarts.ECharts | null = null;
|
|
let customerChart: echarts.ECharts | null = null;
|
|
|
|
const totalStats = reactive({
|
|
totalBudget: '0',
|
|
executed: '0',
|
|
overallRate: '0',
|
|
dailyAverage: '0',
|
|
});
|
|
|
|
const data = ref<DepartmentData[]>([]);
|
|
const trendTableData = ref<TrendData[]>([]);
|
|
const departmentTableData = ref<DepartmentData[]>([]);
|
|
const progressTableData = ref<ProgressData[]>([]);
|
|
const customerTableData = ref<CustomerData[]>([]);
|
|
|
|
const getMockData = (): DepartmentData[] => {
|
|
// 根据图片中的数据生成模拟数据
|
|
const departments = [
|
|
{
|
|
name: '一区一部',
|
|
monthlyBudget: '1300000',
|
|
executed: '937947.12',
|
|
achievementRate: '72.15',
|
|
surplusBudget: '362052.88',
|
|
surplusDays: '21',
|
|
dailyAverage: '17240.61',
|
|
yesterday: '36129.91',
|
|
estimatedAchievement: '130.51%',
|
|
dailyDifference: '18889.30',
|
|
previousDay: '47003.57',
|
|
sequentialRatio: '-10873.66',
|
|
timeProgress: '42%',
|
|
januaryAchievement: '1547156.813',
|
|
januarySequentialRatio: '-16%',
|
|
differenceFromTarget: '-7%',
|
|
},
|
|
{
|
|
name: '一区二部',
|
|
monthlyBudget: '3500000',
|
|
executed: '2338291.84',
|
|
achievementRate: '66.81',
|
|
surplusBudget: '1161708.16',
|
|
surplusDays: '21',
|
|
dailyAverage: '55319.44',
|
|
yesterday: '106494.91',
|
|
estimatedAchievement: '130.71%',
|
|
dailyDifference: '51175.47',
|
|
previousDay: '105791.93',
|
|
sequentialRatio: '702.97',
|
|
timeProgress: '37%',
|
|
januaryAchievement: '3789374.035',
|
|
januarySequentialRatio: '-8%',
|
|
differenceFromTarget: '2%',
|
|
},
|
|
{
|
|
name: '一区三部',
|
|
monthlyBudget: '2100000',
|
|
executed: '1896655.41',
|
|
achievementRate: '90.32',
|
|
surplusBudget: '203344.59',
|
|
surplusDays: '21',
|
|
dailyAverage: '9683.07',
|
|
yesterday: '55613.03',
|
|
estimatedAchievement: '145.93%',
|
|
dailyDifference: '45928.43',
|
|
previousDay: '53994.08',
|
|
sequentialRatio: '1618.95',
|
|
timeProgress: '60%',
|
|
januaryAchievement: '2861643.583',
|
|
januarySequentialRatio: '-27%',
|
|
differenceFromTarget: '-19%',
|
|
},
|
|
{
|
|
name: '一区四部',
|
|
monthlyBudget: '1600000',
|
|
executed: '1960555.94',
|
|
achievementRate: '122.53',
|
|
surplusBudget: '-360555.94',
|
|
surplusDays: '21',
|
|
dailyAverage: '-17169.33',
|
|
yesterday: '66458.76',
|
|
estimatedAchievement: '209.76%',
|
|
dailyDifference: '83628.09',
|
|
previousDay: '79558.31',
|
|
sequentialRatio: '-1309.55',
|
|
timeProgress: '93%',
|
|
januaryAchievement: '2216583.797',
|
|
januarySequentialRatio: '-28%',
|
|
differenceFromTarget: '-20%',
|
|
},
|
|
{
|
|
name: '代理运营总',
|
|
monthlyBudget: '8500000',
|
|
executed: '7134418.26',
|
|
achievementRate: '83.93',
|
|
surplusBudget: '1365581.74',
|
|
surplusDays: '21',
|
|
dailyAverage: '65027.70',
|
|
yesterday: '264596.76',
|
|
estimatedAchievement: '149.32%',
|
|
dailyDifference: '199682.28',
|
|
previousDay: '286347.89',
|
|
sequentialRatio: '-21651.29',
|
|
timeProgress: '54%',
|
|
januaryAchievement: '10414758.23',
|
|
januarySequentialRatio: '-18%',
|
|
differenceFromTarget: '-10%',
|
|
},
|
|
{
|
|
name: '渠道部',
|
|
monthlyBudget: '3000000',
|
|
executed: '29134092.68',
|
|
achievementRate: '97.11',
|
|
surplusBudget: '865907.32',
|
|
surplusDays: '21',
|
|
dailyAverage: '41233.68',
|
|
yesterday: '806116.365',
|
|
estimatedAchievement: '153.54%',
|
|
dailyDifference: '764882.68',
|
|
previousDay: '844507.14',
|
|
sequentialRatio: '-38390.78',
|
|
timeProgress: '67%',
|
|
januaryAchievement: '39078360.25',
|
|
januarySequentialRatio: '-23%',
|
|
differenceFromTarget: '-15%',
|
|
},
|
|
{
|
|
name: '电商一部',
|
|
monthlyBudget: '4700000',
|
|
executed: '3993326.756',
|
|
achievementRate: '83.88',
|
|
surplusBudget: '706673.24',
|
|
surplusDays: '21',
|
|
dailyAverage: '33651.11',
|
|
yesterday: '125785.982',
|
|
estimatedAchievement: '140.02%',
|
|
dailyDifference: '89563.45',
|
|
previousDay: '132741.39',
|
|
sequentialRatio: '-6955.41',
|
|
timeProgress: '54%',
|
|
januaryAchievement: '4522665.91',
|
|
januarySequentialRatio: '4%',
|
|
differenceFromTarget: '15%',
|
|
},
|
|
{
|
|
name: '电商二部',
|
|
monthlyBudget: '2800000',
|
|
executed: '1302679.056',
|
|
achievementRate: '46.52',
|
|
surplusBudget: '1497320.94',
|
|
surplusDays: '21',
|
|
dailyAverage: '7130.10',
|
|
yesterday: '26049.373',
|
|
estimatedAchievement: '66.06%',
|
|
dailyDifference: '-45251.62',
|
|
previousDay: '27892.62',
|
|
sequentialRatio: '-1843.24',
|
|
timeProgress: '17%',
|
|
januaryAchievement: '2776056.70',
|
|
januarySequentialRatio: '1%',
|
|
differenceFromTarget: '12%',
|
|
},
|
|
{
|
|
name: '张哥自营',
|
|
monthlyBudget: '6000000',
|
|
executed: '9770673.61',
|
|
achievementRate: '162.84',
|
|
surplusBudget: '-3770673.61',
|
|
surplusDays: '21',
|
|
dailyAverage: '-179555.89',
|
|
yesterday: '392448.463',
|
|
estimatedAchievement: '300.20%',
|
|
dailyDifference: '572004.35',
|
|
previousDay: '331376.53',
|
|
sequentialRatio: '61071.93',
|
|
timeProgress: '133%',
|
|
januaryAchievement: '8310458.83',
|
|
januarySequentialRatio: '-28%',
|
|
differenceFromTarget: '-20%',
|
|
},
|
|
{
|
|
name: '电商合计',
|
|
monthlyBudget: '13500000',
|
|
executed: '15012801.94',
|
|
achievementRate: '111.21',
|
|
surplusBudget: '-1512801.94',
|
|
surplusDays: '21',
|
|
dailyAverage: '-72038.19',
|
|
yesterday: '544283.818',
|
|
estimatedAchievement: '195.87%',
|
|
dailyDifference: '616316.17',
|
|
previousDay: '492286.58',
|
|
sequentialRatio: '5207.28',
|
|
timeProgress: '81%',
|
|
januaryAchievement: '15609181.44',
|
|
januarySequentialRatio: '-14%',
|
|
differenceFromTarget: '-4%',
|
|
},
|
|
{
|
|
name: '金牛总任务',
|
|
monthlyBudget: '52000000',
|
|
executed: '51280190.4',
|
|
achievementRate: '98.62',
|
|
surplusBudget: '719809.60',
|
|
surplusDays: '21',
|
|
dailyAverage: '34276.65',
|
|
yesterday: '1615096.78',
|
|
estimatedAchievement: '163.84%',
|
|
dailyDifference: '1622865.58',
|
|
previousDay: '1622865.58',
|
|
sequentialRatio: '-7768.79',
|
|
timeProgress: '69%',
|
|
januaryAchievement: '65108327.29',
|
|
januarySequentialRatio: '-20%',
|
|
differenceFromTarget: '-12%',
|
|
},
|
|
];
|
|
|
|
const mockData: DepartmentData[] = departments.map((dept) => ({
|
|
department: dept.name,
|
|
monthlyBudget: dept.monthlyBudget,
|
|
executed: dept.executed,
|
|
achievementRate: dept.achievementRate,
|
|
surplusBudget: dept.surplusBudget,
|
|
surplusDays: dept.surplusDays,
|
|
dailyAverage: dept.dailyAverage,
|
|
yesterday: dept.yesterday,
|
|
estimatedAchievement: dept.estimatedAchievement,
|
|
dailyDifference: dept.dailyDifference,
|
|
previousDay: dept.previousDay,
|
|
sequentialRatio: dept.sequentialRatio,
|
|
timeProgress: dept.timeProgress,
|
|
januaryAchievement: dept.januaryAchievement,
|
|
januarySequentialRatio: dept.januarySequentialRatio,
|
|
differenceFromTarget: dept.differenceFromTarget,
|
|
}));
|
|
|
|
return mockData;
|
|
};
|
|
|
|
const getTrendTableData = (): TrendData[] => {
|
|
const departments = [
|
|
{ name: '一区一部', data: '1487551.85' },
|
|
{ name: '一区二部', data: '3526692.48' },
|
|
{ name: '一区三部', data: '1666811.56' },
|
|
{ name: '一区四部', data: '1551268.30' },
|
|
{ name: '代理运营总', data: '8232324.19' },
|
|
{ name: '渠道部', data: '5976545.88' },
|
|
{ name: '电商一部', data: '2159880.32' },
|
|
{ name: '电商二部', data: '1365144.30' },
|
|
{ name: '张哥自营', data: '5569665.85' },
|
|
{ name: '电商合计', data: '9094690.47' },
|
|
{ name: '金牛总任务', data: '23303560.56' },
|
|
];
|
|
|
|
return departments.map((dept) => ({
|
|
department: dept.name,
|
|
data: dept.data,
|
|
}));
|
|
};
|
|
|
|
const getProgressTableData = (): ProgressData[] => {
|
|
return [
|
|
{
|
|
date: '2026/4/10',
|
|
quarterFirstDay: '2026/4/1',
|
|
quarterLastDay: '2026/6/30',
|
|
textAudit: '2026/4/1',
|
|
homeCount: '91',
|
|
completedCount: '9',
|
|
remainingDays: '82',
|
|
quarterSecondMonth: '2026/5/1',
|
|
textAudit2: '2026/3/31',
|
|
},
|
|
{
|
|
date: '2026/4/10',
|
|
quarterFirstDay: '2026/4/1',
|
|
quarterLastDay: '2026/6/30',
|
|
textAudit: '2026/4/1',
|
|
homeCount: '30',
|
|
completedCount: '9',
|
|
remainingDays: '21',
|
|
quarterSecondMonth: '2026/5/1',
|
|
textAudit2: '2026/3/31',
|
|
},
|
|
];
|
|
};
|
|
|
|
const getCustomerTableData = (): CustomerData[] => {
|
|
return [
|
|
{
|
|
customer: '客户1',
|
|
date1: 18168312,
|
|
date2: 18628392,
|
|
date3: 3985,
|
|
date4: 20786,
|
|
date5: 191584,
|
|
date6: 359278,
|
|
date7: 3870546,
|
|
date8: 3873374,
|
|
date9: 4457089,
|
|
date10: 4295086,
|
|
date11: 2672587,
|
|
date12: 1569225,
|
|
date13: 114875,
|
|
date14: 279896,
|
|
date15: 396185,
|
|
date16: 291112,
|
|
date17: 215208,
|
|
date18: 0,
|
|
date19: 0,
|
|
date20: 0,
|
|
},
|
|
{
|
|
customer: '客户2',
|
|
date1: 12345678,
|
|
date2: 87654321,
|
|
date3: 12345,
|
|
date4: 54321,
|
|
date5: 987654,
|
|
date6: 456789,
|
|
date7: 2345678,
|
|
date8: 8765432,
|
|
date9: 3456789,
|
|
date10: 9876543,
|
|
date11: 4567890,
|
|
date12: 1234567,
|
|
date13: 234567,
|
|
date14: 345678,
|
|
date15: 456789,
|
|
date16: 567890,
|
|
date17: 678901,
|
|
date18: 789012,
|
|
date19: 890123,
|
|
date20: 901234,
|
|
},
|
|
{
|
|
customer: '客户3',
|
|
date1: 98765432,
|
|
date2: 23456789,
|
|
date3: 98765,
|
|
date4: 56789,
|
|
date5: 1234567,
|
|
date6: 7654321,
|
|
date7: 3456789,
|
|
date8: 9876543,
|
|
date9: 4567890,
|
|
date10: 12345678,
|
|
date11: 87654321,
|
|
date12: 23456789,
|
|
date13: 9876543,
|
|
date14: 4567890,
|
|
date15: 1234567,
|
|
date16: 7654321,
|
|
date17: 3456789,
|
|
date18: 9876543,
|
|
date19: 4567890,
|
|
date20: 1234567,
|
|
},
|
|
];
|
|
};
|
|
|
|
const calculateTotalStats = (_data: DepartmentData[]) => {
|
|
totalStats.totalBudget = '52000000';
|
|
totalStats.executed = '51280190.4';
|
|
totalStats.overallRate = '98.62';
|
|
totalStats.dailyAverage = '1654199.70';
|
|
};
|
|
|
|
const initTrendChart = () => {
|
|
nextTick(() => {
|
|
if (!trendChartRef.value) return;
|
|
|
|
if (trendChart) trendChart.dispose();
|
|
trendChart = echarts.init(trendChartRef.value);
|
|
|
|
// 生成动态日期数据
|
|
const dates: string[] = [];
|
|
const today = new Date();
|
|
for (let i = 15; i >= 0; i--) {
|
|
const date = new Date(today);
|
|
date.setDate(today.getDate() - i);
|
|
dates.push(`${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`);
|
|
}
|
|
|
|
// 使用图片中的数据模式,生成动态数据
|
|
const executedData = [
|
|
2149518.05, 1871119.64, 2088655.19, 1702786.8, 1770269.31, 1714822.74, 1649665.0, 1622865.58, 1615096.78, 1485551.17, 1407212.21, 1364826.0,
|
|
1233123.83, 1089680.69, 1086898.47, 816321.92,
|
|
];
|
|
const targetData = dates.map(() => 1733333.33); // 52000000 / 30
|
|
|
|
trendChart.setOption({
|
|
title: {
|
|
text: '预算执行趋势',
|
|
left: 'center',
|
|
},
|
|
tooltip: {
|
|
trigger: 'axis',
|
|
formatter: '{b}<br/>{a}: ¥{c}',
|
|
},
|
|
legend: {
|
|
data: ['实际执行', '目标'],
|
|
bottom: 0,
|
|
},
|
|
xAxis: {
|
|
type: 'category',
|
|
data: dates,
|
|
axisLabel: {
|
|
rotate: 45,
|
|
},
|
|
},
|
|
yAxis: {
|
|
type: 'value',
|
|
name: '金额',
|
|
axisLabel: {
|
|
formatter: '¥{value}',
|
|
},
|
|
},
|
|
series: [
|
|
{
|
|
name: '实际执行',
|
|
type: 'line',
|
|
data: executedData,
|
|
areaStyle: {},
|
|
},
|
|
{
|
|
name: '目标',
|
|
type: 'line',
|
|
data: targetData,
|
|
lineStyle: {
|
|
type: 'dashed',
|
|
},
|
|
},
|
|
{
|
|
name: '执行柱状图',
|
|
type: 'bar',
|
|
data: executedData,
|
|
itemStyle: {
|
|
color: 'rgba(64, 158, 255, 0.5)',
|
|
},
|
|
},
|
|
],
|
|
});
|
|
});
|
|
};
|
|
|
|
const initDepartmentChart = (data: DepartmentData[]) => {
|
|
nextTick(() => {
|
|
if (!departmentChartRef.value) return;
|
|
|
|
if (departmentChart) departmentChart.dispose();
|
|
departmentChart = echarts.init(departmentChartRef.value);
|
|
|
|
// 提取部门和预算数据用于饼图
|
|
const pieData = data.map((item) => ({
|
|
name: item.department,
|
|
value: parseFloat(item.monthlyBudget.replace(/,/g, '')),
|
|
}));
|
|
|
|
departmentChart.setOption({
|
|
title: {
|
|
text: '部门预算分布',
|
|
left: 'center',
|
|
},
|
|
tooltip: {
|
|
trigger: 'item',
|
|
formatter: '{a} <br/>{b}: ¥{c} ({d}%)',
|
|
},
|
|
legend: {
|
|
orient: 'vertical',
|
|
right: '5%',
|
|
top: 'center',
|
|
data: data.map((item) => item.department),
|
|
textStyle: {
|
|
fontSize: 10,
|
|
},
|
|
},
|
|
series: [
|
|
{
|
|
name: '预算',
|
|
type: 'pie',
|
|
radius: ['30%', '60%'],
|
|
center: ['40%', '50%'],
|
|
data: pieData,
|
|
emphasis: {
|
|
itemStyle: {
|
|
shadowBlur: 10,
|
|
shadowOffsetX: 0,
|
|
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
|
},
|
|
},
|
|
},
|
|
],
|
|
});
|
|
});
|
|
};
|
|
|
|
const initProgressChart = () => {
|
|
nextTick(() => {
|
|
if (!progressChartRef.value) return;
|
|
|
|
if (progressChart) progressChart.dispose();
|
|
progressChart = echarts.init(progressChartRef.value);
|
|
|
|
// 任务进度雷达图数据
|
|
const indicator = [
|
|
{ name: '预算执行', max: 100 },
|
|
{ name: '时间进度', max: 100 },
|
|
{ name: '部门达成率', max: 100 },
|
|
{ name: '日均完成', max: 100 },
|
|
{ name: '目标达成', max: 100 },
|
|
];
|
|
|
|
progressChart.setOption({
|
|
title: {
|
|
text: '任务进度分析',
|
|
left: 'center',
|
|
},
|
|
tooltip: {},
|
|
legend: {
|
|
data: ['实际进度', '目标进度'],
|
|
bottom: 0,
|
|
},
|
|
radar: {
|
|
indicator: indicator,
|
|
radius: '60%',
|
|
center: ['50%', '50%'],
|
|
},
|
|
series: [
|
|
{
|
|
name: '任务进度',
|
|
type: 'radar',
|
|
data: [
|
|
{
|
|
value: [98.62, 69, 85.3, 95.2, 98.62],
|
|
name: '实际进度',
|
|
areaStyle: {
|
|
color: 'rgba(64, 158, 255, 0.3)',
|
|
},
|
|
},
|
|
{
|
|
value: [100, 100, 100, 100, 100],
|
|
name: '目标进度',
|
|
areaStyle: {
|
|
color: 'rgba(255, 159, 64, 0.3)',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
});
|
|
};
|
|
|
|
const initCustomerChart = () => {
|
|
nextTick(() => {
|
|
if (!customerChartRef.value) return;
|
|
|
|
if (customerChart) customerChart.dispose();
|
|
customerChart = echarts.init(customerChartRef.value);
|
|
|
|
// 生成日期数据
|
|
const dates = [
|
|
'2026/2/7',
|
|
'2026/2/8',
|
|
'2026/2/9',
|
|
'2026/2/10',
|
|
'2026/2/11',
|
|
'2026/2/12',
|
|
'2026/2/15',
|
|
'2026/2/16',
|
|
'2026/2/17',
|
|
'2026/2/18',
|
|
'2026/2/19',
|
|
'2026/2/20',
|
|
'2026/2/21',
|
|
'2026/2/22',
|
|
'2026/2/23',
|
|
'2026/2/24',
|
|
'2026/2/25',
|
|
'2026/2/26',
|
|
'2026/2/27',
|
|
'2026/2/28',
|
|
];
|
|
|
|
// 模拟客户数据趋势
|
|
const customerData = [
|
|
18168312, 18628392, 3985, 20786, 191584, 359278, 3870546, 3873374, 4457089, 4295086, 2672587, 1569225, 114875, 279896, 396185, 291112, 215208,
|
|
0, 0, 0,
|
|
];
|
|
|
|
customerChart.setOption({
|
|
title: {
|
|
text: '客户数据趋势',
|
|
left: 'center',
|
|
},
|
|
tooltip: {
|
|
trigger: 'axis',
|
|
formatter: '{b}<br/>{a}: {c}',
|
|
},
|
|
legend: {
|
|
data: ['客户数据'],
|
|
bottom: 0,
|
|
},
|
|
xAxis: {
|
|
type: 'category',
|
|
data: dates,
|
|
axisLabel: {
|
|
rotate: 45,
|
|
},
|
|
},
|
|
yAxis: {
|
|
type: 'value',
|
|
name: '数值',
|
|
},
|
|
series: [
|
|
{
|
|
name: '客户数据',
|
|
type: 'line',
|
|
data: customerData,
|
|
areaStyle: {},
|
|
itemStyle: {
|
|
color: '#409eff',
|
|
},
|
|
},
|
|
{
|
|
name: '数据柱状图',
|
|
type: 'bar',
|
|
data: customerData,
|
|
itemStyle: {
|
|
color: 'rgba(64, 158, 255, 0.5)',
|
|
},
|
|
},
|
|
],
|
|
});
|
|
});
|
|
};
|
|
|
|
const openSettingDialog = () => {
|
|
dialogVisible.value = true;
|
|
};
|
|
|
|
const openDepartmentTaskDialog = () => {
|
|
departmentTaskDialogVisible.value = true;
|
|
};
|
|
|
|
const handleSaveSettings = () => {
|
|
dialogVisible.value = false;
|
|
};
|
|
|
|
const saveDepartmentTasks = () => {
|
|
departmentTaskDialogVisible.value = false;
|
|
};
|
|
|
|
const handleSearch = () => {
|
|
data.value = getMockData();
|
|
departmentTableData.value = data.value;
|
|
trendTableData.value = getTrendTableData();
|
|
progressTableData.value = getProgressTableData();
|
|
customerTableData.value = getCustomerTableData();
|
|
calculateTotalStats(data.value);
|
|
initTrendChart();
|
|
initDepartmentChart(data.value);
|
|
initProgressChart();
|
|
initCustomerChart();
|
|
};
|
|
|
|
const handleReset = () => {
|
|
searchParams.dateRange = [];
|
|
searchParams.department = '';
|
|
handleSearch();
|
|
};
|
|
|
|
const handleCustomerSearch = () => {
|
|
// 这里可以根据搜索参数过滤数据
|
|
customerTableData.value = getCustomerTableData();
|
|
initCustomerChart();
|
|
};
|
|
|
|
const handleCustomerReset = () => {
|
|
customerSearchParams.customer = '';
|
|
customerSearchParams.status = '';
|
|
handleCustomerSearch();
|
|
};
|
|
|
|
onMounted(() => {
|
|
handleSearch();
|
|
window.addEventListener('resize', () => {
|
|
trendChart?.resize();
|
|
departmentChart?.resize();
|
|
progressChart?.resize();
|
|
customerChart?.resize();
|
|
});
|
|
});
|
|
|
|
watch(activeTab, (newTab) => {
|
|
nextTick(() => {
|
|
if (newTab === 'trend') {
|
|
initTrendChart();
|
|
} else if (newTab === 'department') {
|
|
initDepartmentChart(data.value);
|
|
} else if (newTab === 'progress') {
|
|
initProgressChart();
|
|
} else if (newTab === 'customer') {
|
|
initCustomerChart();
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
|
|
<style scoped>
|
|
.ads-summary-monitor {
|
|
padding: 20px;
|
|
}
|
|
|
|
.card-header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.setting-btn {
|
|
color: #409eff;
|
|
}
|
|
|
|
.search-container {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.search-form {
|
|
display: flex;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.search-form :deep(.el-form-item) {
|
|
margin-right: 12px;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.ads-summary-monitor :deep(.el-select) {
|
|
width: 180px;
|
|
}
|
|
|
|
.ads-summary-monitor :deep(.el-select__wrapper),
|
|
.ads-summary-monitor :deep(.el-select__selected-item),
|
|
.ads-summary-monitor :deep(.el-select__placeholder) {
|
|
color: #303133;
|
|
}
|
|
|
|
.tabs-container {
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.tabs-container :deep(.el-tabs__header) {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.chart-container {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.chart {
|
|
width: 100%;
|
|
height: 600px;
|
|
min-height: 600px;
|
|
}
|
|
|
|
.table-container {
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.dialog-footer {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 8px;
|
|
}
|
|
</style>
|